react-hooks useEffect() 清理仅用于 componentWillUnmount?

IT技术 reactjs react-hooks
2021-04-06 22:39:54

让我解释一下这段代码的结果,以便轻松询问我的问题。

const ForExample = () => {
    const [name, setName] = useState('');
    const [username, setUsername] = useState('');

    useEffect(() => {
        console.log('effect');
        console.log({
            name,
            username
        });

        return () => {
            console.log('cleaned up');
            console.log({
                name,
                username
            });
        };
    }, [username]);

    const handleName = e => {
        const { value } = e.target;

        setName(value);
    };

    const handleUsername = e => {
        const { value } = e.target;

        setUsername(value);
    };

    return (
        <div>
            <div>
                <input value={name} onChange={handleName} />
                <input value={username} onChange={handleUsername} />
            </div>
            <div>
                <div>
                    <span>{name}</span>
                </div>
                <div>
                    <span>{username}</span>
                </div>
            </div>
        </div>
    );
};

ForExample component坐骑,“效果”将被记录。这与componentDidMount().

每当我更改名称输入时,都会记录“效果”和“清理”。反之亦然,自从我添加[username]useEffect(). 这与componentDidUpdate()

最后,当ForExample component卸载时,将记录“已清理”。这与componentWillUnmount().

我们都知道。

总而言之,只要重新渲染组件(包括卸载),就会调用“清理”

如果我想使这个组件要记录的,只有当它是卸载的瞬间“清理”,我只需要改变的第二个参数useEffect()[]

但是如果我更改[username][],则ForExample component不再实现componentDidUpdate()for 名称输入。

我想要做的是,使组件componentDidUpdate()仅支持名称输入和componentWillUnmount(). (仅在卸载组件时记录“已清理”)

6个回答

您可以使用多个 useEffect()。

例如,如果我的变量是data1,我可以在我的组件中使用所有这些:

useEffect( () => console.log("mount"), [] );
useEffect( () => console.log("data1 update"), [ data1 ] );
useEffect( () => console.log("any update") );
useEffect( () => () => console.log("data1 update or unmount"), [ data1 ] );
useEffect( () => () => console.log("unmount"), [] );
所以,如果你创建一个 useEffect 钩子,它返回一个函数 .. 那么返回函数之前的代码作为 componentDidMount 运行......并且返回函数中的代码被调用为 componentWillUnmount?这有点令人困惑,所以请确保我理解正确。useEffect(()=>{ // 挂载运行的代码 ... return()=> { // 卸载运行的代码}}) 是吗?
2021-06-02 22:39:54
我建议阅读overreacted.io/a-complete-guide-to-useeffect 思考生命周期中的钩子并不是那么甜蜜
2021-06-09 22:39:54
谢谢@Georgy 得到它最后一个 useEffect 正在返回回调看不清楚
2021-06-10 22:39:54
@siluverukirankumar 回调的返回值(函数)是在销毁(卸载事件)时调用的。这就是为什么最后一个例子是一个 HOC,立即返回函数。第二个参数是 React 将在何处查找更改以重新运行此钩子。当它是一个空数组时,它只会运行一次。
2021-06-19 22:39:54
first useEffects 和 last useEffects 有什么区别,first useEffect 会在willmountdidmount上被调用,last useEffect 被返回的回调函数为空数组,为什么?您能否详细说明我们何时以及如何使用每个 useEffect 用例?
2021-06-20 22:39:54

由于清理不依赖于username,您可以将清理放在一个单独的中useEffect,并给出一个空数组作为第二个参数。

例子

const { useState, useEffect } = React;

const ForExample = () => {
  const [name, setName] = useState("");
  const [username, setUsername] = useState("");

  useEffect(
    () => {
      console.log("effect");
    },
    [username]
  );

  useEffect(() => {
    return () => {
      console.log("cleaned up");
    };
  }, []);

  const handleName = e => {
    const { value } = e.target;

    setName(value);
  };

  const handleUsername = e => {
    const { value } = e.target;

    setUsername(value);
  };

  return (
    <div>
      <div>
        <input value={name} onChange={handleName} />
        <input value={username} onChange={handleUsername} />
      </div>
      <div>
        <div>
          <span>{name}</span>
        </div>
        <div>
          <span>{username}</span>
        </div>
      </div>
    </div>
  );
};

function App() {
  const [shouldRender, setShouldRender] = useState(true);

  useEffect(() => {
    setTimeout(() => {
      setShouldRender(false);
    }, 5000);
  }, []);

  return shouldRender ? <ForExample /> : null;
}

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

<div id="root"></div>

漂亮干净的例子。我想知道。我可以在更改导航时以某种方式触发使用效果,还是必须在组件树中将其向上移动?因为在粘贴“清理过的” useEffect 时,我没有看到这个触发器。
2021-06-04 22:39:54

为了添加到已接受的答案中,我遇到了类似的问题,并使用类似的方法和下面的人为示例解决了它。在这种情况下,我需要记录一些参数,componentWillUnmount并且如原始问题中所述,我不希望每次更改参数时都记录。

const componentWillUnmount = useRef(false)

// This is componentWillUnmount
useEffect(() => {
    return () => {
        componentWillUnmount.current = true
    }
}, [])

useEffect(() => {
    return () => {
        // This line only evaluates to true after the componentWillUnmount happens 
        if (componentWillUnmount.current) {
            console.log(params)
        }
    }

}, [params]) // This dependency guarantees that when the componentWillUnmount fires it will log the latest params
useEffects 的顺序确实很重要,如果其他人想知道
2021-06-03 22:39:54
function LegoComponent() {

  const [lego, setLegos] = React.useState([])

  React.useEffect(() => {
    let isSubscribed = true
    fetchLegos().then( legos=> {
      if (isSubscribed) {
        setLegos(legos)
      }
    })
    return () => isSubscribed = false
  }, []);

  return (
    <ul>
    {legos.map(lego=> <li>{lego}</li>)}
    </ul>
  )
}

在上面的代码中, fetchLegos 函数返回一个Promise。我们可以通过在 useEffect 范围内设置条件来“取消”promise,防止应用程序在组件卸载后设置状态。

警告:无法对卸载的组件执行 React 状态更新。这是一个空操作,但它表明您的应用程序中存在内存泄漏。要修复,请取消 useEffect 清理函数中的所有订阅和异步任务。

我所做的不是创建太多复杂的函数和方法,而是创建一个事件侦听器并自动为我完成挂载和卸载,而不必担心手动操作。这是一个例子。

//componentDidMount
useEffect( () => {

    window.addEventListener("load",  pageLoad);

    //component will unmount
    return () => {
       
        window.removeEventListener("load", pageLoad);
    }

 });

既然这部分已经完成,我就可以像这样从 pageLoad 函数中运行我想要的任何东西。

const pageLoad = () =>{
console.log(I was mounted and unmounted automatically :D)}