#reactjs #react-hooks
#reactjs #реагирующие крючки
Вопрос:
У меня есть hook useInterval, который автоматически загружает данные каждые 10 секунд, однако у меня также есть кнопка, которая может вручную загружать данные в любой момент. Я изо всех сил пытаюсь перезапустить интервальный таймер при нажатии кнопки. Таким образом, в основном, если интервал равен 5, но я тем временем нажимаю кнопку, интервал должен перезапуститься и снова начать отсчет до 10 перед загрузкой данных
const useInterval = (callback, delay) => {
const savedCallback = useRef(callback);
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
useEffect(() => {
const tick = () => {
savedCallback.current();
}
if (delay !== null) {
const id = setInterval(tick, delay);
return () => clearInterval(id);
}
}, [delay]);
};
export default useInterval;
ЧАСТЬ ПРИЛОЖЕНИЯ:
useInterval(() => {
getMessage();
}, 10000)
const getMessage = async () => {
setProcessing(true)
try {
const res = await fetch('url')
const response = await res.json();
setRecievedData(response)
}
catch (e) {
console.log(e)
}
finally {
setProcessing(false)
}
}
const getMessageManually = () => {
getMessage()
RESTART INTERVAL
}
Ответ №1:
Вы должны добавить функцию сброса, возвращающую значение из перехвата.
Я также исправил несколько проблем и добавил обработчик размонтирования:
// Usage
const resetInterval = useInterval(() => ..., DELAY);
resetInterval();
// Implementation
const useInterval = (callback, delay) => {
const savedCallbackRef = useRef(callback);
const intervalIdRef = useRef();
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
// handle tick
useEffect(() => {
const tick = () => {
savedCallback.current();
};
if (delay !== null) {
intervalIdRef.current = setInterval(tick, delay);
}
const id = intervalIdRef.current;
return () => {
clearInterval(id);
};
}, [delay]);
// handle unmount
useEffect(() => {
const id = intervalIdRef.current;
return () => {
clearInterval(id);
};
}, []);
const resetInterval = useCallback(() => {
clearInterval(intervalIdRef.current);
intervalIdRef.current = setInterval(savedCallback.current, delay)
}, [delay]);
return resetInterval;
};
Комментарии:
1. Сброс фактически останавливается, но не запускает новый интервал, но я понял, как это сделать, благодаря вашему ответу, и теперь все работает нормально. Спасибо!
Ответ №2:
Вы можете добавить функцию сброса в перехват и вернуть эту функцию. Функция сброса должна очистить существующий интервал и запустить новый.
Вот код для хука, который можно сбросить и остановить.
const useInterval = (callback, delay) => {
const savedCallback = useRef(callback);
const intervalRef = useRef(null);
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
useEffect(() => {
if (delay !== null) {
const id = setInterval(savedCallback.current, delay);
intervalRef.current = id;
return () => clearInterval(id);
}
}, [delay]);
useEffect(()=>{
// clear interval on when component gets removed to avoid memory leaks
return () => clearInterval(intervalRef.current);
},[])
const reset = useCallback(() => {
if(intervalRef.current!==null){
clearInterval(intervalRef.current);
intervalRef.current = setInterval(savedCallback.current,delay)
}
});
const stop = useCallback(() => {
if(intervalRef.current!==null){
clearInterval(intervalRef.current);
}
})
return {
reset,
stop
};
};
// usage
const {reset,stop} = useInterval(()=>{},10000);
reset();
stop();
Комментарии:
1. Всего несколько ошибок:
clearInterval(intervalRef.current)
— У вас нет ссылки при размонтировании,intervalRef!==null
всегда правдиво2. @DennisVash Спасибо. Исправлено. Но
clearInterval(intervalRef.current)
работает. Что здесь не так.
Ответ №3:
Другим решением является удаление ссылки при обратном вызове, что заставляет перехват перезапускать счетчик при каждом изменении обратного вызова
итак, обновляя вышеупомянутое решение
// Реализация const useInterval = (обратный вызов, задержка) => {
const intervalIdRef = useRef();
// обработайте галочку useEffect(() => { const tick = () => { обратный вызов(); };
if (delay !== null) {
intervalIdRef.current = setInterval(tick, delay);
}
const id = intervalIdRef.current;
return () => {
clearInterval(id);
};
}, [задержка]);
// дескриптор размонтировать useEffect(() => { const id = intervalIdRef.current; return () => { clearInterval(id); }; }, []); };
И тогда вы можете использовать его следующим образом
const [counter, setCounter] = useState[0]
const onTimerFinish = useCallback(() => {
setCounter(counter 1)
// setCounter will reset the interval
}, [counter])
useResetInterval(() => {
onTimerFinish()
}, 5000)