如何使用 `onClick` 事件停止调用由 useEffect 挂钩调用的函数?

How to use an `onClick` event to stop calling a function which is called by the useEffect hook?

如何使用 onClick 事件来停止循环函数调用?由于使用 useEffectsetIntervalclearInterval.

,函数调用重复出现

this article中显示的示例,下面的代码将永远运行。单击 <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;

我会像这样从 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;

添加另一个名为 running 的状态,并使 useEffect 依赖于它。每当 running 切换为 false 时,间隔将被 useEffect 的清理功能清除。由于 runningfalse,我们跳过设置新间隔。每当切换到 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>

我认为停止是指对点击事件说 clearInterval。所以最好的方法是在一个新的状态变量中保存一个间隔 id。


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.

范围之外定义的变量中