簡體   English   中英

從兩個不同的useEffect調用方法(改變狀態)

[英]Calling a method (that mutates state) from two different 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狀態變量,但其依賴項列表不包括它。 將其包含在依賴項列表中將創建無限循環。

您可以通過使用setCounter函數類型參數來消除對counter的依賴。

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>
  );
}

嘗試使用功能性setState, setState((state, props) => stateChange)

useEffect(() => {
  if (clicked) {
    setCounterAndSetUrlHash(counter => counter + 1);
    setTimeout(() => {
      setClicked(false);
    }, 2000);
  }
}, [clicked]);

為了使用第一個計數器值解決onhashchange回調的問題,我建議將功能移到setCounter的回調中。 這也意味着按鈕和哈希更改需要其他功能。

還要在頂部以及useEffect之后設置變量和useState定義,以便可以使用它們。 如果您希望useEffect只運行一次,請設置一個空數組依賴項; 忽略依賴項將在每個渲染器上運行。

export const App = () => {
    const [clicked, setClicked] = useState(false);
    const [counter, setCounter] = useState(0);

    /**
     * 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) {
            setCounter(counter => {
                const value = counter + 1;

                if (value === 0) {
                    window.location.hash = "";
                } else {
                    window.location.hash = String(value);
                }

                return value;
            });

            setTimeout(() => {
                setClicked(false);
            }, 2000);
        }
    }, [clicked]);

    /**
     * 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 = e => {
            const value = Number(window.location.hash.replace("#", ""));

            // must be number
            if (typeof value === "number" && value % 1 === 0) {
                setCounter(counter => {

                    if (counter - 1 !== value) {
                        return counter;
                    }

                    if (value === 0) {
                        window.location.hash = "";
                    } else {
                        window.location.hash = String(value);
                    }

                    return value;
                });
            }
        };
    }, []);

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

向第一個useEffect()添加計數器:

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

  useEffect(() => {
    if (clicked) {
      setCounterAndSetUrlHash(counter + 1);
      setTimeout(() => {
        setClicked(false);
      }, 2000);
    }
  }, [clicked, counter]);

https://codesandbox.io/s/cold-sun-56u7j

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM