Это первый день моего участия в Gengwen Challenge, смотрите подробности мероприятия:Обновить вызов
предисловие
Дело вот в чем, работать сверхурочно по выходным, чтобы наверстать упущенное по проектам, есть функция синхронных данных как асинхронный процесс, и нужно написать опрос, чтобы получить синхронные результаты. Эта функция проста, я знаком с опросом!
功能代码是使用 react hooks 写的,setInterval 并没有如我所愿的实现轮询的功能,然后我怀疑人生了? ? ?
анализ проблемы
В связи с острой потребностью я временно изменил код в виде компонента класса и перевыпустил версию, и проблема была решена~
Но в прошлом такого не могло быть, я должен думать дальше, зачем использовать setInterval для Waterloo и цепляться за него?
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
let id = setInterval(() => {
setCount(count + 1);
}, 1000);
return () => clearInterval(id);
});
return <div>{count}</div>;
}
实际上上面的代码是有问题的,React 默认会在每次渲染时,都重新执行 useEffect。 ЗвонокclearInterval
После повторногоsetInterval
Решать проблему
Друзья, которые использовали хуки, должны знать, что у useEffect есть второй параметр, передающий массив зависимостей, который может повторно выполнять эффект при изменении массива зависимостей, а не выполнять его каждый раз при его рендеринге.
то если мы перейдем в пустой массив[]
Как зависимость, чтобы подкомпонент выполнялся при его монтировании и очищался при уничтожении компонента, может ли это решить проблему?
function Counter() {
let [count, setCount] = useState(0);
useEffect(() => {
let id = setInterval(() => {
setCount(count + 1);
}, 1000);
return () => clearInterval(id);
}, []);
return <div>{count}</div>;
}
Но на самом деле после обновления таймера до 1 он останавливается. Таймер по-прежнему дает сбой, и функция опроса не может быть реализована.
Почему явление не такое, как ожидалось? На самом деле, если вы присмотритесь, то обнаружите, что это закрытая яма!
Счетчик, используемый useEffect, получается при первом рендеринге.При приобретении это0
. Поскольку эффект повторно не выполнялся, поэтомуsetInterval
используется в закрытияхcount
всегда с первого рендера, так что естьcount + 1
всегда1
Феномен. Тебя вдруг осенило! Если вы хотите получить запомненное количество хуков, вы не забудете использовать useRef в это время, и оно должно появиться на сцене~
useRef, хуки с памятью
Через две вышеупомянутые неудачи мы суммируем два противоречия, которые мы обнаружили:
1. useEffect не имеет памяти, каждый раз, когда он выполняется, он очищает предыдущий эффект и устанавливает новый эффект. Новый эффект получает новые реквизиты и состояние;
2, setInterval не забуду, это были бы ссылки на старые реквизиты и состояние, если бы оно не менялось. Но если его заменить, он сбросит время;
- установить таймер
setInterval(fn, delay)
,вfn
передачаsavedCallback
. - Первый рендер, набор
savedCallback
дляcallback1
- Второй рендер, набор
savedCallback
дляcallback2
- ......
Давайте попробуем переписать его с помощью useRef :
function Counter() {
let [count, setCount] = useState(0);
const savedCallback = useRef();
function callback() {
// 可以读取到最新的 state 和 props
setCount(count + 1);
}
// 每次渲染,更新ref为最新的回调
useEffect(() => {
savedCallback.current = callback;
});
useEffect(() => {
let id = setInterval(() => {
savedCallback.current();
}, 1000);
return () => clearInterval(id);
}, []);
return <div>{count}</div>;
}
С одной стороны, его познакомили с[]
Наш эффект не будет выполнен, поэтому таймер не будет сброшен. С другой стороны, поскольку настройкаsavedCallback
Ref, мы можем добраться до параметров обратного вызова из последнего рендеринга, а затем вызовите, когда запускается таймер. Он имеет более низкую память данных, проблема была решена, но это слишком много проблем, читаемость бедна!
useInterval
function Counter() {
const [count, setCount] = useState(0);
useInterval(() => {
setCount(count + 1);
}, 1000);
return <div>{count}</div>;
}
Поэтому мы настроили хук для извлечения логики и для лучшей семантики назвали его useInterval.
function useInterval(callback) {
const savedCallback = useRef();
useEffect(() => {
savedCallback.current = callback;
});
useEffect(() => {
function tick() {
savedCallback.current();
}
let id = setInterval(tick, 1000);
return () => clearInterval(id);
}, []);
}
Значение задержки здесь жестко запрограммировано, нам нужно его параметризовать, учитывая, что еслиdelay
Изменено, мы также должны перезапустить таймер, поэтому нам нужно поставить задержку в зависимости от использования. Изменить это:
function useInterval(callback,delay) {
const savedCallback = useRef();
useEffect(() => {
savedCallback.current = callback;
});
useEffect(() => {
function tick() {
savedCallback.current();
}
let id = setInterval(tick, delay);
return () => clearInterval(id);
}, [delay]);
}
Ну, теперь нам не нужно обращать внимание на эту кучу бессвязной логики, чтобы использовать таймеры в хуках, нам достаточно использовать useInterval вместо setInterval.
Но что, если вы хотите приостановить таймер? Это очень просто, нам нужно только изменить логику задержки. Когда задержка равна нулю, нам не нужно устанавливать таймер. Давайте изменим его снова:
// 最终版
function useInterval(callback,delay) {
const savedCallback = useRef();
useEffect(() => {
savedCallback.current = callback;
});
useEffect(() => {
function tick() {
savedCallback.current();
}
if (delay !== null) {
let id = setInterval(tick, delay);
return () => clearInterval(id);
}
}, [delay]);
}
function Counter() {
const [count, setCount] = useState(0);
const [delay, setDelay] = useState(1000);
const [isRunning, setIsRunning] = useState(true);
useInterval(() => {
setCount(count + 1);
}, isRunning ? delay : null);
return <div>{count}</div>;
}
Теперь наш useInterval может обрабатывать все возможные изменения: изменение значения задержки, пауза и возобновление, что намного мощнее исходного setInterval!
Суммировать
Хуки и класс — это два разных режима программирования, мы можем столкнуться с некоторыми странными проблемами при использовании хуков, но не паникуйте, нам нужно найти основную причину проблемы, а затем изменить свое мышление, чтобы решить ее, вместо использования старое Мышление.
Наконец, спасибо, что дочитали до этого места, я иду менять код опроса, увидимся!