Как использовать событие onClick, чтобы прекратить вызывать функцию, которая вызывается ловушкой useEffect? - PullRequest
1 голос
/ 17 апреля 2020

Как использовать событие onClick для остановки повторяющегося вызова функции? Вызов функции повторяется из-за использования useEffect, setInterval и clearInterval.

. В примере, показанном в этой статье , приведенный ниже код будет работать вечно. Как остановить вызов функции после нажатия <header></header>?

import React, { useState, useEffect } from 'react';

const IntervalExample = () => {
  const [seconds, setSeconds] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setSeconds(seconds => seconds + 1);
    }, 1000);
    return () => clearInterval(interval);
  }, []);

  return (
    <div className="App">
      <header className="App-header">
        {seconds} seconds have elapsed since mounting.
      </header>
    </div>
  );
};

export default IntervalExample;

Ответы [ 3 ]

1 голос
/ 17 апреля 2020

Добавьте еще одно состояние с именем running и сделайте useEffect зависимым от него. Всякий раз, когда running переключается на false, интервал будет очищаться функцией очистки useEffect. Поскольку running равно false, мы пропускаем установку нового интервала. Всякий раз, когда он переключается на true, он будет перезапущен.

const { useState, useEffect, useCallback } = React;

const IntervalExample = () => {
  const [seconds, setSeconds] = useState(0);
  const [running, setRunning] = useState(true);
  
  const toggleRunning = useCallback(
    () => setRunning(run => !run)
  , []);

  useEffect(() => {
    if(!running) {
      // setSeconds(0); // if you want to reset it as well
      return;
    }
    
    const interval = setInterval(() => {
      setSeconds(seconds => seconds + 1);
    }, 1000);
    return () => clearInterval(interval);
  }, [running]);

  return (
    <div className="App">
      <header className="App-header">
        {seconds} seconds have elapsed since mounting.
      </header>
      
      <button
        onClick={toggleRunning}
        >
        {running ? 'running' : 'stopped'}
      </button>
    </div>
  );
};

ReactDOM.render(
  <IntervalExample />,
  root
);
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>
1 голос
/ 17 апреля 2020

Я думаю, что, остановив, вы хотите сказать clearInterval о событии клика. Так что лучший способ сохранить идентификатор интервала в новой переменной состояния.


const IntervalExample = () => {
  const [seconds, setSeconds] = useState(0);
  const [currentIntervalId, setIntervalId] = useState(null);

  useEffect(() => {
    const interval = setInterval(() => {
      setSeconds(seconds => seconds + 1);
    }, 1000);
    setIntervalId(interval);
    return () => clearInterval(interval);
  }, []);

  const stopInterval = () => {
    clearInterval(currentIntervalId);
  };

  return (
    <div className="App">
      <header className="App-header" onClick={stopInterval}>
        {seconds} seconds have elapsed since mounting.
      </header>
    </div>
  );
};
  • Почему в состоянии? Потому что это даст вам больше гибкости, чтобы перезапустить интервал снова или остановить, когда вы хотите, основываясь на событии.

Примечание: Вы также можете поместить это в переменную, определенную вне область действия useEffect.

1 голос
/ 17 апреля 2020

Я бы вытащил интервал const из функции useEffect следующим образом.

import React, { useState, useEffect } from 'react';

const IntervalExample = () => {
  const [seconds, setSeconds] = useState(0);
  let interval;

  useEffect(() => {
    interval = setInterval(() => {
      setSeconds(seconds => seconds + 1);
    }, 1000);
    return () => clearInterval(interval);
  }, []);


  const handleClick = () => {
    if (interval) {
      clearInterval(interval);
    }
  }

  return (
    <div className="App">
      <header className="App-header" onClick={() => handleClick()}>
        {seconds} seconds have elapsed since mounting.
      </header>
    </div>
  );
};

export default IntervalExample;
...