89

如何使用useEffect钩子(或任何其他钩子)进行复制componentWillUnmount

在传统的类组件中,我会做这样的事情:

class Effect extends React.PureComponent {
    componentDidMount() { console.log("MOUNT", this.props); }
    componentWillUnmount() { console.log("UNMOUNT", this.props); }
    render() { return null; }
}

useEffect钩子:

function Effect(props) {
  React.useEffect(() => {
    console.log("MOUNT", props);

    return () => console.log("UNMOUNT", props)
  }, []);

  return null;
}

(完整示例:https ://codesandbox.io/s/2oo7zqzx1n )

这不起作用,因为返回的“清理”函数会useEffect捕获安装期间的道具,而不是卸载期间道具的状态。

如何在每次道具更改时运行函数体(或清理)的useEffect情况下获得最新版本的道具?

一个类似的问题没有解决访问最新道具的部分。

反应文档状态:

如果你想运行一个效果并且只清理一次(在挂载和卸载时),你可以传递一个空数组([])作为第二个参数。这告诉 React 你的效果不依赖于任何来自 props 或 state 的值,所以它永远不需要重新运行。

但是在这种情况下,我依赖于道具......但仅限于清理部分......

4

4 回答 4

63

您可以使用 useRef 并将要使用的道具存储在闭包中,例如渲染 useEffect 返回回调方法

function Home(props) {
  const val = React.useRef();
  React.useEffect(
    () => {
      val.current = props;
    },
    [props]
  );
  React.useEffect(() => {
    return () => {
      console.log(props, val.current);
    };
  }, []);
  return <div>Home</div>;
}

演示

然而,更好的方法是将第二个参数传递给,useEffect以便清理和初始化发生在所需道具的任何更改上

React.useEffect(() => {
  return () => {
    console.log(props.current);
  };
}, [props.current]);
于 2019-03-13T10:33:35.903 回答
9

useLayoutEffect() 是你在 2021 年的答案

useLayoutEffect(() => {
    return () => {
        // Your code here.
    }
}, [])

这相当于 ComponentWillUnmount。

99% 的时间你想使用 useEffect,但是如果你想在卸载 DOM 之前执行任何操作,那么你可以使用我提供的代码。

于 2021-04-01T12:08:12.363 回答
1

useLayoutEffect非常适合清理 DOM 节点上的 eventListener。

否则,使用常规useEffectref.current 将在触发时间钩子时为空

更多关于反应文档https://reactjs.org/docs/hooks-reference.html#uselayouteffect

  import React, { useLayoutEffect, useRef } from 'react';

  const audioRef = useRef(null);


  useLayoutEffect(() => {
    if (!audioRef.current) return;

    const progressEvent = (e) => {
      setProgress(audioRef.current.currentTime);
    };

    audioRef.current.addEventListener('timeupdate', progressEvent);

    return () => {
      try {
        audioRef.current.removeEventListener('timeupdate', progressEvent);
      } catch (e) {
        console.warn('could not removeEventListener on timeupdate');
      }
    };
  }, [audioRef.current]);



将 ref 附加到组件 DOM 节点

<audio ref={audioRef} />

于 2022-02-10T14:28:57.160 回答
0
useEffect(() => {
  if (elements) {
    const cardNumberElement =
      elements.getElement('cardNumber') ||  // check if we already created an element
      elements.create('cardNumber', defaultInputStyles); // create if we did not
            
    cardNumberElement.mount('#numberInput');
  }
}, [elements]);
于 2021-06-04T17:58:28.723 回答