#javascript #reactjs
Вопрос:
Я создал пользовательский крючок в своем приложении React, который передает несколько функций тому, к Component
которому прикреплен крючок. Эти функции используются для обновления носителей в зависимости от того, считает ли себя крюк mounted
.
Однако я столкнулся с проблемой, когда, когда я возвращаю функцию ( play
) из своего крючка, а затем вызываю эту функцию, она обновляет данные на основе начального состояния крючка (несмотря на то, что состояние обновлялось много раз).
const usePlayer = () => {
const audioRef = useRef(__GLOBAL_AUDIO);
const [state, setState] = useReducer((state, newState) => ({...state, ...newState}), {
mounted: false,
playing: false,
});
const play = () => ensureMounted(() => {
audioRef.current.play();
});
const ensureMounted = callback => {
// This if statement will never run as this function does not
// seem to get any state other than the initially supplied one
if(state.mounted){
callback();
return;
}
// ensureMounted will always skip to this portion of the function
// despite what the current state is
audioRef.current.src = cdn("/link/to/new/src.mp3");
onLoad(callback);
};
useEffect(() => {
// This function is tied to redux, but is irrelevant. Just know
// that mounted does change from true to false and vice-versa - often.
const _mounted = checkToSeeIfMounted();
setState({ mounted: _mounted });
}, deps);
return {
playing: state.playing,
play: play,
};
};
Теперь очевидно, что этот код был чрезвычайно упрощен, но я надеюсь, что он все еще отражает проблему, с которой я сталкиваюсь. У меня есть подозрение, что проблема заключается в том, что React может передавать старую(?) ссылку на функцию при каждом повторном рендеринге, но я не уверен, а официальные документы настолько расплывчаты в отношении «продвинутых» функций, что я не уверен, как их использовать или будут ли они вообще полезны.
Любая помощь приветствуется, ура.
Комментарии:
1. В чем ваша проблема?
2. @Viet Мне кажется, что я как можно яснее объяснил, в чем моя проблема.
3. Я тоже так не думаю. Что, по-твоему , должно произойти, чего нет? Почему вы думаете, что это должно произойти, а не что происходит? Также, почему вы полностью изменили этот вопрос — у него был рабочий пример, и вы от него избавились.
4. @Jamiec Я думаю , что должно произойти, когда состояние будет обновлено (т. Е. когда
mounted
обновляется), функция должна действовать соответствующим образом при вызове компонентом. Проблема в том, что давайте предположим, что мы вызываемensureMounted
несколько раз из компонента в течение его срока службы, исходное состояние изменится — и все жеensureMounted
будет действовать только на основе начального состояния.5. @Jamiec Я обновил его, чтобы он был более четким и кратким. Я чувствую, что оригинал был слишком притуплен, чтобы иметь смысл (мне нравится делать вопросы как можно более простыми, поскольку пользователи StackOverflow, похоже, избегают всего, что длиннее нескольких строк кода).
Ответ №1:
Ваш код, похоже, работает так, как ожидалось. Очевидных проблем со ссылками нет. (ПРАВКА: По крайней мере, в коде вашего исходного вопроса).
Помните, что пользователь hook
работает на «том же уровне» компонента, где он используется. Другими словами, это то же самое, как если бы вы использовали пользовательский хук useState
непосредственно в своем компоненте. Действия, создающие ваш пользовательский хук «повторный рендеринг», также приведут к повторному рендерингу вашего компонента.
Помните, что изменения состояния обычно происходят асинхронно. Иногда это может привести к сбивающему с толку поведению.
Когда ваш код запускается в первый раз, оба useEffect
крючка выполняются по порядку. Первой useEffect
будет выполняться только один раз, потому что он не имеет никаких зависимостей ( hook.example
ссылка внутри него всегда будет первой ссылки (в конце концов), но он запускается только один раз, поэтому у вас не возникнет любой вопрос, если вы сделать некоторые другие вещи внутри этого обратного вызова (например, при добавлении прослушивателей событий или выполнения некоторых асинхронных такс) или звоните hook.example
(тем самым изменяя исходное состояние) до useEffect
трассы).
Продолжая, после hook.example
вызова регистрируется текущее значение данных ( false
), затем запрашивается обновление состояния (асинхронно), выполнение кода продолжается, и useEffect
запускается второй, регистрирующий текущее значение ( false
). Наконец, состояние обновляется, и ваш пользовательский хук «повторно визуализируется», что также приводит к повторной визуализации вашего компонента. Теперь первый useEffect
не срабатывает, а второй useEffect
срабатывает, потому hook.data
что изменился. Наконец, регистрируется новое значение данных ( true
).
При звонке hook.example
по нажатию кнопки проблем нет, потому hook.example
что там всегда находится последняя ссылка.
Пожалуйста, также обратите внимание, что хорошая практика требует, чтобы ваш первый useEffect
крючок был hook
в качестве зависимости (тогда логика возможной фильтрации должна выполняться внутри useEffect
обратного вызова), и в вашем случае добавление hook
в массив зависимостей приведет к бесконечному циклу. Возможно, ваш фактический код отличается, но это хорошо знать.
Если вам нужны дополнительные разъяснения, не стесняйтесь комментировать.
const useHook = () => {
const [data, setData] = React.useState(false);
const example = () =>
preCallCheck(() => {
setData(!data);
});
const preCallCheck = (callback) => {
console.log('preCallCheck()t', data);
callback();
};
return {
data: data,
example: example
};
};
const Parent = () => {
const hook = useHook();
React.useEffect(() => {
hook.example();
}, []);
React.useEffect(() => {
console.log('useEffect()t', hook.data);
}, [hook.data]);
return (
<div>
<h1>{'Hello, World!'}</h1>
<button type={'button'} onClick={() => hook.example()}>{'Click Me'}</button>
</div>
);
};
ReactDOM.render(<Parent />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Комментарии:
1. Приношу извинения за первоначальный отказ, я случайно нажал «Принять», а затем, должно быть, случайно нажал «отклонить», когда я отказался. Просто заметил, когда вы отредактировали заново: -) К счастью, похоже, у меня все еще было время удалить его- lol
2. Спасибо за ответ, но, к сожалению, это мне не очень помогло, кроме как доказать, что мой код работает на StackOverflow, но не в моем приложении 🙁
3. @ГРОВЕР. Я увидел ваш новый опубликованный код и думаю, что ваша проблема, скорее всего, заключалась в том, что вы использовали свой собственный крючок, а не в самом крючке, который, похоже, в порядке. Вы уверены, что ваше
mounted
состояние в конечном итоге становится истинным?4. Абсолютно уверен. Когда я войду
_mounted
вuseEffect
систему, он вернет иtrue
то, иfalse
другое, в зависимости от условий.5. @ГРОВЕР. и если сделать
console.log(state.mounted)
это прямо перед вашимreturn
заявлением внутри вашего крючка, всегда ли вы получаете желаемую ценность? Если это так, то проблема в использовании крючка.