Реагирующие крючки используют сброс интервала после нажатия кнопки

#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)