我们如何使用react钩子实现componentWillUnmount?

时间:2019-04-21 19:34:40

标签: javascript reactjs react-hooks

在挂载和销毁组件之前,在 之前立即调用方法componentWillUnmount()。如果我们将useEffect与空数组([])一起用作第二个参数,并将函数放在return语句中,则它将在组件卸载后甚至在另一个组件安装后执行。据我所知,这样做是出于性能原因。为了不延迟渲染。

所以问题是-我们如何在挂载组件之前使用钩子调用某些函数?

我要做的是一个应用程序,该应用程序可以在用户键入时保存用户的输入(无需提交表单)。我使用setInterval每N秒保存一次更新的文本。而且我需要在组件卸载之前强制保存更新。我不想在导航之前使用React Router的提示。这是电子应用。感谢您对实现此类功能的任何想法或建议。

更新

不幸的是,Effects with Cleanup在让浏览器绘制之后运行。可以在以下位置找到更多详细信息:So What About Cleanup?。基本上,这意味着清除是在卸载组件之后运行的,与执行componentWillUnmount()中的代码不同。如果将console.log语句放入清理代码和其他组件中,则可以清楚地看到调用的顺序。问题是我们是否可以在使用钩子卸载组件之前执行之前执行某些代码。

Update2

如我所见,我应该更好地描述我的用例。让我们想象一个理论上的应用程序,它将其数据保存在Redux存储中。而且我们有两个具有某种形式的组件。为简单起见,我们没有任何后端或任何异步逻辑。我们仅将Redux存储用作数据存储。

我们不想在每次击键时都更新Redux存储。因此,我们将实际值保持在本地组件的状态中,当组件安装时,将使用商店中的值进行初始化。我们还创建一个效果,将setInterval设置为1s。

我们有以下过程。用户键入一些内容。更新将以本地组件状态存储,直到调用我们的setInterval回调。回调只是将数据放入存储中(调度动作)。我们将回调放入useEffect返回语句中,以在卸载组件时强制保存存储,因为在这种情况下我们希望尽快保存数据进行存储。

当用户在第一个组件中键入内容并立即转到第二个组件(快于1s)时,就会出现问题。由于在重新渲染后将调用第一个组件中的清理,因此在安装第二个组件之前不会更新我们的商店。因此,第二个组件将过时的值恢复到其本地状态。

如果我们将回调函数放在componentWillUnmount()中,它将在卸载之前被调用,并且存储将在下一个组件装入之前被更新。那么我们可以使用钩子来实现它吗?

8 个答案:

答案 0 :(得分:4)

可以通过在componentWillUnmount挂钩内返回一个函数来模拟

useEffect。返回的函数将在每次重新呈现组件之前被调用。严格来说,这是同一回事,但是您应该能够模拟使用此行为所需的任何行为。

useEffect(() => {
  const unsubscribe = api.createSubscription()
  return () => unsubscribe()
})

请参阅同一问题here的详细说明。

答案 1 :(得分:3)

我同意 Frank 的观点,但代码需要如下所示,否则它只会在第一次渲染时运行:

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

这相当于 ComponentWillUnmount

答案 2 :(得分:2)

对此进行了一些调查,得出的结论是,当前的钩子根本无法提供componentWillUnmount的替代方案。因此,如果您有一个需要它的用例(至少对我而言,至少对我来说是这样),则集成非反应库,您只需要按旧方法进行操作并使用组件即可。

希望这种情况将来会改变

答案 3 :(得分:1)

在组件卸载之前或之后调用从useEffect返回的函数都没关系:您仍然可以通过闭包访问有价值的状态:

  const [input, setInput] = useState(() => Store.retrieveInput());

  useEffect(() => {
    return () => Store.storeInput(input); // < you can access "input" here, even if the component unmounted already
  }, []);

如果您不以组件状态管理输入,则整个结构将损坏,应更改为在正确的位置管理状态。在这种情况下,您应该将组件的共享输入状态提升到父级。

答案 4 :(得分:1)

类似于@pritam 的回答,但有一个抽象的代码示例。 useRef 的整个想法是允许您跟踪回调的更改,并且在执行时不会有过时的闭包。因此,底部的 useEffect 可以有一个空的依赖数组,以确保它只在组件卸载时运行。 See the code demo

可重复使用的钩子:

type Noop = () => void;

const useComponentWillUnmount = (callback: Noop) => {
    const mem = useRef<Noop>();

    useEffect(() => {
        mem.current = callback;
    }, [callback]);

    useEffect(() => {
        return () => {
            const func = mem.current as Noop;
            func();
        };
    }, []);
};

代码示例:

答案 5 :(得分:0)

ReactJS文档在钩子上指定以下内容:

  

效果还可以选择指定如何在效果之后“清理”   返回一个函数。

因此,在卸载组件时以及由于后续渲染而重新运行效果之前,将执行您在useEffect钩中返回的所有函数。

答案 6 :(得分:0)

经过一些研究,发现-您仍然可以完成此操作。有点棘手,但应该可以。

您可以利用useRef并将要使用的道具存储在闭包中,例如render 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>;
}

DEMO

但是,更好的方法是将第二个参数传递给useEffect,以便在所需道具的任何更改时进行清理和初始化

React.useEffect(() => {
  return () => {
    console.log(props.current);
  };
}, [props.current]);

答案 7 :(得分:0)

自从引入useLayoutEffect挂钩以来,您现在就可以这样做

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

模拟componentWillUnmount。这将在卸载期间运行,但要在元素实际离开页面之前进行。