反应钩useEffect()清理仅componentWillUnmount吗?


83

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

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()。(仅在卸载组件时记录“清理”)


4
您可以有2个单独的效果。一个给定一个数组username作为第二个参数,另一个给定一个空数组作为第二个参数。
Tholle

@Tholle您的意思是我必须进行2个单独的useEffect()方法吗?
koo

1
是的,这是解决问题的一种方法。
Tholle

1
@Tholle我认为最后一个useEffect()方法会覆盖它。我会尽力。谢谢
koo19年

2
大!别客气。这取决于清理操作。2个单独的效果不是一个坏解决方案。
Tholle

Answers:


80

由于清理操作不依赖于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时,我看不到此触发器。
Fabian Bosler

112

您可以使用多种用途

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

useEffect( () => console.log("mount"), [] );
useEffect( () => console.log("will update data1"), [ data1 ] );
useEffect( () => console.log("will update any") );
useEffect( () => () => console.log("will update data1 or unmount"), [ data1 ] );
useEffect( () => () => console.log("unmount"), [] );

4
first和last useEffects有什么区别,将在willmountdidmount上调用first useEffect,最后返回useEffect返回带有空数组的回调函数,为什么?您能否详细说明每个useEffect用例何时以及如何使用?
siluveru kiran kumar,

3
@siluverukirankumar回调的返回值(函数)是在destroy(卸载事件)上被调用的东西。这就是为什么最后一个示例是HOC,立即返回函数的原因。第二个参数是React将在其中寻找更改以重新运行此挂钩的地方。当它是一个空数组时,它将只运行一次。
乔治,

3
感谢@Georgy得到了最后一次useEffect返回的回调未清晰可见
siluveru kiran kumar

2
因此,如果您创建一个useEffect钩子,并且它返回一个函数..则返回的函数之前的代码将作为componentDidMount ...运行,并且返回的函数中的代码将被componentWillUnmount调用?这有点令人困惑,所以请确保我理解正确。useEffect(()=> {//要在mount上运行的代码... return()=> {//要运行dismount的代码}})对吗?
Maiya

我建议阅读overreacted.io/a-complete-guide-to-useeffect 关于生命周期中的钩子的思考并不那么甜蜜
-moto

2
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范围内设置条件来“取消”承诺,从而防止应用程序在组件卸载后设置状态。

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


0

useEffect在其自己的范围内被隔离,并相应地呈现。图片来自https://reactjs.org/docs/hooks-custom.html

在此处输入图片说明


欢迎使用指向解决方案的链接,但是请确保没有该链接的情况下,您的回答是有用的:在链接周围添加上下文,以便您的其他用户可以了解它的含义和含义,然后引用您所使用页面中最相关的部分如果目标页面不可用,请重新链接。只是链接的答案可能会被删除
22БогданОпир

0

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

//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)}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.