代码之家  ›  专栏  ›  技术社区  ›  roeland

引用React useEffect挂钩中的过期状态

  •  6
  • roeland  · 技术社区  · 6 年前

    我想把州保存到 localStorage 卸载组件时。 componentWillUnmount

    我试着用同样的方法来处理这个问题 useEffect 钩子,但在的返回函数中状态似乎不正确 使用效果 .

    为什么呢?如何在不使用类的情况下保存状态?

    下面是一个虚拟示例。按“关闭”时,结果始终为0。

    import React, { useState, useEffect } from "react";
    import ReactDOM from "react-dom";
    
    function Example() {
      const [tab, setTab] = useState(0);
      return (
        <div>
          {tab === 0 && <Content onClose={() => setTab(1)} />}
          {tab === 1 && <div>Why is count in console always 0 ?</div>}
        </div>
      );
    }
    
    function Content(props) {
      const [count, setCount] = useState(0);
    
      useEffect(() => {
        // TODO: Load state from localStorage on mount
    
        return () => {
          console.log("count:", count);
        };
      }, []);
    
      return (
        <div>
          <p>Day: {count}</p>
          <button onClick={() => setCount(count - 1)}>-1</button>
          <button onClick={() => setCount(count + 1)}>+1</button>
          <button onClick={() => props.onClose()}>close</button>
        </div>
      );
    }
    
    ReactDOM.render(<Example />, document.querySelector("#app"));
    

    CodeSandbox

    3 回复  |  直到 6 年前
        1
  •  2
  •   giggo1604    5 年前

    我试图用useEffect钩子做同样的事情,但useEffect的返回函数中的状态似乎不正确。

    原因是关闭。闭包是函数对其作用域中变量的引用。你的 useEffect 当组件装载时,回调只运行一次,因此返回回调引用初始计数值0。

    这里给出的答案是我的建议。我推荐@Jed Richard的传球答案 [count] 使用效果 ,具有向 localStorage 只有当计数改变时。这比每次更新都不传递任何内容要好。除非您非常频繁地(每隔几毫秒)更改计数,否则您不会看到性能问题,也可以编写 本地存储 count

    useEffect(() => { ... }, [count]);
    

    如果你坚持只给我写信 本地存储 卸载时,您可以使用一个丑陋的hack/解决方案-refs。基本上,您将创建一个变量,该变量存在于组件的整个生命周期中,您可以从组件中的任何位置引用它。但是,您必须手动将状态与该值同步,这非常麻烦。Refs不会给出上面提到的闭包问题,因为Refs是具有 current useRef 将返回相同的对象。只要你能使基因变异 .current 价值,你的 使用效果 始终(仅)可以读取最新的值。

    CodeSandbox link

    const {useState, useEffect, useRef} = React;
    
    function Example() {
      const [tab, setTab] = useState(0);
      return (
        <div>
          {tab === 0 && <Content onClose={() => setTab(1)} />}
          {tab === 1 && <div>Count in console is not always 0</div>}
        </div>
      );
    }
    
    function Content(props) {
      const value = useRef(0);
      const [count, setCount] = useState(value.current);
    
      useEffect(() => {
        return () => {
          console.log('count:', value.current);
        };
      }, []);
    
      return (
        <div>
          <p>Day: {count}</p>
          <button
            onClick={() => {
              value.current -= 1;
              setCount(value.current);
            }}
          >
            -1
          </button>
          <button
            onClick={() => {
              value.current += 1;
              setCount(value.current);
            }}
          >
            +1
          </button>
          <button onClick={() => props.onClose()}>close</button>
        </div>
      );
    }
    
    ReactDOM.render(<Example />, document.querySelector('#app'));
    <script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>
    
    <div id="app"></div>
        2
  •  0
  •   forlayo    4 年前

    使用React的useRef,这将起作用,但它并不漂亮:

    function Content(props) {
      const [count, setCount] = useState(0);
      const countRef = useRef();
      // this effect fires every time count is changed
      useEffect(() => () => {
        countRef.current = count;
      },[count]);
    
      // this effect fires as per a true componentWillUnmount
      useEffect(() => () => {
        console.log("count:", countRef.current);
      }, []);
    }
    

    请注意(在我看来)更容易忍受的一点函数,该函数为useEffect返回函数的代码构造。