首页 > 解决方案 > 从两个不同的 useEffect 调用一个方法(改变状态)

问题描述

我正在玩 React Hooks,从两个不同的 useEffect 调用一个方法(改变状态)。给出以下代码:

function App() {
  const [clicked, setClicked] = useState(false);

  /**
   * Listen for clicked changes. When clicked changes to true,
   * allow setCounterAndSetUrlHash to do it's thing, before accpeting
   * the next click. So the clicked flag is simply a valve, that opens
   * after two seconds.
   */
  useEffect(() => {
    if (clicked) {
      setCounterAndSetUrlHash(counter + 1);
      setTimeout(() => {
        setClicked(false);
      }, 2000);
    }
  }, [clicked]);

  const [counter, setCounter] = useState(0);

  /**
   * Listen for changes in the URL hash. When the user presses
   * the back button in the browser toolbar, decrement the
   * counter value.
   */
  useEffect(() => {
    window.onhashchange = () => {
      const value = Number(window.location.hash.replace("#", ""));
      // must be number
      if (typeof value === "number" && value % 1 === 0) {
        if (counter - 1 === value) {
          setCounterAndSetUrlHash(counter - 1);
        }
      }
    };
  });

  /**
   * Set a new counter value and apply the same value
   * to the URL hash. I want to reuse this function
   * in both useEffect above.
   */
  const setCounterAndSetUrlHash = value => {
    setCounter(value);
    if (value === 0) {
      window.location.hash = "";
    } else {
      window.location.hash = String(value);
    }
  };

  return (
    <div className="App">
      <p>Clicked: {String(clicked)}</p>
      <p>Counter: {counter}</p>
      <button type="button" onClick={() => setClicked(true)}>
        Click me
      </button>
    </div>
  );
}

实际代码: https ://codesandbox.io/s/dreamy-shadow-7xesm

该代码实际上正在工作。但是我收到了这个警告..

React Hook useEffect 缺少依赖项:'counter'。包括它或删除依赖数组。(react-hooks/exhaustive-deps)

..我不确定如何在保持当前功能的同时符合这一要求。当我向依赖项添加计数器时,我最终会出现一个无限循环。

标签: javascriptreactjsreact-hooks

解决方案


您的第一个效果使用counter状态变量,但它的依赖项列表不包括它。将其包含在依赖项列表中将创建无限循环。

您可以counter通过在setCounter.

function App() {
  const [clicked, setClicked] = useState(false);

  /**
   * Listen for clicked changes. When clicked changes to true,
   * allow setCounterAndSetUrlHash to do it's thing, before accpeting
   * the next click. So the clicked flag is simply a valve, that opens
   * after two seconds.
   */
  useEffect(() => {
    if (clicked) {
      incrCounter(1);
      setTimeout(() => {
        setClicked(false);
      }, 2000);
    }
  }, [clicked]);

  const [counter, setCounter] = useState(0);

  /**
   * Listen for changes in the URL hash. When the user presses
   * the back button in the browser toolbar, decrement the
   * counter value.
   */
  useEffect(() => {
    window.onhashchange = () => {
      const value = Number(window.location.hash.replace("#", ""));
      // must be number
      if (typeof value === "number" && value % 1 === 0) {
        if (counter - 1 === value) {
          incrCounter(- 1);
        }
      }
    };
  });

  useEffect(() => {
    if (counter === 0) {
      window.location.hash = "";
    } else {
      window.location.hash = String(counter);
    }
  }, [counter])
  /**
   * Set a new counter value and apply the same value
   * to the URL hash. I want to reuse this function
   * in both useEffect above.
   */
  const incrCounter = delta => {
    setCounter(value => value + delta);
  };

  return (
    <div className="App">
      <p>Clicked: {String(clicked)}</p>
      <p>Counter: {counter}</p>
      <button type="button" onClick={() => setClicked(true)}>
        Click me
      </button>
    </div>
  );
}

推荐阅读