在useEffect

时间:2019-06-26 14:34:52

标签: reactjs asynchronous react-hooks

在异步clickHandler中卸载组件后调用setState时,会在控制台中引起警告:

Warning: Can't perform a React state update on an unmounted component.

请参见Codesandbox Example

function App() {
  const [show, setShow] = useState(true)
  return {show && <Button
          text="Click to remove"
          clickHandler={() => setShow(false)}
        /> }
}
function Button({ text, clickHandler }) {
  const [state, setState] = useState(text);

  const handleClick = async () => {
    await clickHandler();
    setState("I was clicked");
  };

  return <button onClick={handleClick}> {state}</button>;
}

单击click to remove按钮将卸载该按钮,然后更新状态。

目标是在异步调用之后 设置按钮组件的状态。该异步调用可以但不必触发按钮被卸载。我正在寻找一种在异步调用后退出setState的方法。

我看不到如何使用useEffect很好地避免这种情况。

该示例中有两种可能的解决方法。 人们使用useRef检查组件是否已卸载(如#14369 (comment)中的建议)。这感觉不太像反应。

另一个解决方法是使用推荐的useEffect保护变量(例如#14369 (comment))。但是要使clickHandler脱离useEffect,clickHandler会存储在一个状态中。但是,要使一个函数进入状态,必须将其包装在另一个函数中,因为setState的{​​{1}}函数将调用作为参数给出的函数。

1 个答案:

答案 0 :(得分:0)

这是一个很好的问题,我不知道为什么它没有引起任何注意。我相信我找到了一个不错的解决方案,如果您认为这可以解决问题,请告诉我。该解决方案基于以下事实:我们可以使用useRef()而无需在任何地方使用ref属性。

我们定义了两个自定义钩子useIsMountedRefuseStateWithMountCheck。您可以像使用useState钩子一样使用后者,如果不再安装组件,它将忽略所有要求的状态更改。

function useIsMountedRef(){
  const ref = useRef(null);
  ref.current = true;

  useEffect(() => {
    return () => {
      ref.current = false;
    }
  });

  return ref;
}

function useStateWithMountCheck(...args){
  const isMountedRef = useIsMountedRef();
  const [state, originalSetState] = useState(...args);

  const setState = (...args) => {
    if (isMountedRef.current) {
      originalSetState(...args);
    }
  }

  return [state, setState];
}

Checkout the Sandbox.