Между setInterval
и setTimeout
есть различия, которые вы, возможно, не захотите потерять, если всегда перезапускаете таймер при повторном рендеринге компонента. Эта скрипта показывает разницу в дрейфе между ними, когда другой код также работает. (В старых браузерах / машинах - например, когда я изначально отвечал на этот вопрос - вам даже не нужно моделировать большие вычисления, чтобы увидеть, как через несколько секунд начнется значительный дрейф.)
Ссылаясь теперь на ваш ответ, Марко, использование setInterval
полностью потеряно, потому что эффекты без условий удаляются и запускаются повторно каждый раз компонент перерисовывается. Итак, в вашем первом примере использование зависимости current
приводит к тому, что этот эффект удаляется и запускается повторно при каждом изменении current
(каждый раз, когда выполняется интервал). Второй делает то же самое, но на самом деле каждый раз, когда изменяется какое-либо состояние (вызывая повторную визуализацию), что может привести к некоторому неожиданному поведению. Единственная причина, по которой он работает, - это то, что next()
вызывает изменение состояния.
Учитывая тот факт, что вы, вероятно, не заботитесь о точном времени, проще всего использовать setTimeout
простым способом, используя переменные current
и auto
в качестве зависимостей. Итак, чтобы повторить часть вашего ответа, сделайте следующее:
useEffect(
() => {
if (!auto) return;
const interval = setTimeout(_ => {
next();
}, autoInterval);
return _ => clearTimeout(interval);
},
[auto, current]
);
Как правило, для тех, кто просто читает этот ответ и хочет способ сделать простой таймер, вот версия, которая не принимает во внимание исходный код OP, а также их потребность в способе запуска и остановки таймера независимо:
const [counter, setCounter] = useState(0);
useEffect(
() => {
const id= setTimeout(() => {
setCounter(counter + 1);
// You could also do `setCounter((count) => count + 1)` instead.
// If you did that, then you wouldn't need the dependency
// array argument to this `useEffect` call.
}, 1000);
return () => {
clearTimeout(id);
};
},
[counter],
);
Однако вам может быть интересно, как использовать более точный интервал, учитывая тот факт, что setTimeout
может дрейфовать больше, чем setInterval
. Вот один из методов, опять же общий без использования кода OP:
// Using refs:
const [counter, setCounter] = useState(30);
const r = useRef(null);
r.current = { counter, setCounter };
useEffect(
() => {
const id = setInterval(() => {
r.current.setCounter(r.current.counter + 1);
}, 1000);
return () => {
clearInterval(id);
};
},
[] // empty dependency array
);
// Using the function version of `setCounter` is cleaner:
const [counter, setCounter] = useState(30);
useEffect(
() => {
const id = setInterval(() => {
setCounter((count) => count + 1);
}, 1000);
return () => {
clearInterval(id);
};
},
[] // empty dependency array
);
Вот что происходит выше:
(первый пример с использованием ссылок): чтобы обратный вызов setInterval
всегда ссылался на текущую приемлемую версию setCounter
, нам нужно какое-то изменяемое состояние. React дает нам это с useRef
. Функция useRef
вернет объект со свойством current
. Затем мы можем установить это свойство (что будет происходить каждый раз при повторном рендеринге компонента) для текущих версий counter
и setCounter
.
(второй пример, с использованием функционального setCounter
): та же идея, что и в первом, за исключением того, что когда мы используем функциональную версию setCounter
, у нас будет доступ к текущей версии счетчика в качестве первого аргумента для функция. Не нужно использовать ссылку, чтобы держать вещи в курсе.
(оба примера, продолжение). Затем, чтобы интервал не удалялся при каждом рендеринге, мы добавляем пустой массив зависимостей в качестве второго аргумента в useEffect
. Интервал все равно будет очищен при размонтировании компонента.
Примечание. Раньше мне нравилось использовать ["once"]
в качестве массива зависимостей, чтобы указать, что я принудительно настраиваю этот эффект только один раз. В то время он был удобен для чтения, но я больше не использую его по двум причинам. Во-первых, в наши дни хуки стали более понятными, и мы повсюду видели пустой массив. Во-вторых, это противоречит очень популярному правилу хуков линтер, которое довольно строго касается того, что входит в массив зависимостей.
Итак, применив то, что мы знаем к исходному вопросу OP, вы можете использовать setInterval
для слайд-шоу с меньшей вероятностью дрейфа, например:
// ... OP's implementation code including `autoInterval`,
// `auto`, and `next` goes above here ...
const r = useRef(null);
r.current = { next };
useEffect(
() => {
if (!auto) return;
const id = setInterval(() => {
r.current.next();
}, autoInterval);
return () => {
clearInterval(id);
};
},
[auto]
);
person
Don
schedule
31.12.2018
useEffect
имеет функцию возврата, которая очищает его, он будет запускать и останавливать новые часы при каждой визуализации.setTimeout
в этом случае должен работать точно так же - person azium   schedule 31.12.2018start
, но безрезультатно - person marco alves   schedule 31.12.2018useEffect has a return function which clears it, it will start and stop a new clock every render.
неверно согласно reactjs.org/docs/hooks-reference.html#useeffect - person marco alves   schedule 31.12.2018if (auto) { id = setInterval(...) }
как это ты имеешь в виду? - person azium   schedule 31.12.2018start
- person marco alves   schedule 31.12.2018useEffect
, который говорит, при каких изменениях значения он не должен запускаться - person azium   schedule 31.12.2018useEffect
или какие-либо хуки в этом отношении внутри тела другой функции или за условным выражением. они должны безоговорочно запускаться в основном теле рендеринга - person azium   schedule 31.12.2018cleaning up an event
раздел - person marco alves   schedule 31.12.2018the previous effect is cleaned up before executing the next effect.
- person azium   schedule 31.12.2018setInterval
внутри своейuseEffect
функции, тогда он не запускает таймер в фоновом режиме, который вы просите - person azium   schedule 31.12.2018The clean-up function runs before the component is removed from the UI to prevent memory leaks.
- person marco alves   schedule 31.12.2018