繁体   English   中英

使用 useEffect 挂钩时无法对未安装的组件执行 React state 更新

[英]Can't perform a React state update on an unmounted component when using useEffect hook

使用useEffect挂钩时,我似乎收到以下错误。

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

我相信这与我调用的异步 function 有关,以设置用户是否通过身份验证。

保护路线.tsx

export function ProtectedRoute({ ...routeProps }: ProtectedRouteProps): ReactElement | null {
    const context = useContext(AppContext);
    const [isAuthenticated, setIsAuthenticated] = useState(false);

    useEffect(() => {
        isUserAuthenticated(context.token).then(setIsAuthenticated).catch(setIsAuthenticated);
    });

    if (isAuthenticated) {
        return <Route {...routeProps} />;
    } else {
        return <Redirect to={{ pathname: "login" }} />;
    }
}

const isUserAuthenticated = async (token: any): Promise<boolean> => {
    try {
        const response = await VerifyAuthentication(token);
        console.log("VerifyAuthentication", response);
        if (response.status !== 200) {
            return false;
        }

        return true;
    } catch (err) {
        return false;
    }
};

应用程序.tsx

class App extends Component<Props, State> {
    renderRouter = () => {
        return (
            <Router>
                <Switch>
                    <ProtectedRoute exact path="/" component={Dashboard} />
                    <Route exact path="/login" component={Login} />
                </Switch>
            </Router>
        );
    };

    render(): ReactElement {
        return (
            <div className="App">
                <AppProvider>
                    <Theme>
                        <Sidebar>{this.renderRouter()}</Sidebar>
                    </Theme>
                </AppProvider>
            </div>
        );
    }
}

据推测,这会将用户重定向到没有此组件的路由:

return <Redirect to={{ pathname: "login" }} />;

这意味着组件已卸载,或者通常从活动使用/内存中卸载。 总是会发生,因为这种情况永远不会true

if (isAuthenticated) {

因为当组件首次呈现时,该值被显式设置为false

const [isAuthenticated, setIsAuthenticated] = useState(false);

所以基本上发生的事情是:

  1. 您触发异步操作以检查用户是否已通过身份验证。
  2. 在等待响应之前,您决定用户未通过身份验证并重定向他们。
  3. 该组件已卸载,因为用户已离开该页面。
  4. 收到异步响应并尝试为不再加载/安装的组件更新 state。

目前还不完全清楚该组件是如何适应您的整体结构的,但是您将需要更改该结构。 要么检查身份验证需要同步,要么您需要等待异步操作完成后再重定向。 后者的一个例子可以很简单:

export function ProtectedRoute({ ...routeProps }: ProtectedRouteProps): ReactElement | null {
  const context = useContext(AppContext);
  const [isAuthenticated, setIsAuthenticated] = useState(false);
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    isUserAuthenticated(context.token)
      .then(x => {
        setIsAuthenticated(x);
        setIsLoading(false);
      })
      .catch(e => {
        setIsAuthenticated(false);
        setIsLoading(false);
        console.log(e);
      });
  });

  if (isLoading) {
    return <div>Loading...</div>;
  } else if (isAuthenticated) {
    return <Route {...routeProps} />;
  } else {
    return <Redirect to={{ pathname: "login" }} />;
  }
}

在这种情况下,isLoading 的一个单独的isLoading值用于跟踪异步操作是否仍在进行,因此组件“等待”数据加载完毕后再决定是否重定向用户。

但总的来说,我不明白为什么身份验证检查不能同步。 更高级别的东西,例如将整个应用程序结构包装在<App/>中的提供者组件,可以具有与上面相同的逻辑,本质上执行异步操作并将结果保存在 state 中。然后可以通过useContext或提供 state Redux 甚至只是作为道具传递给所有子组件。

您不需要在子组件中一遍又一遍地重新检查身份验证。 这是一个应用程序级别的问题。

您可以在调用setIsAuthenticated时使用变量来检查组件是否挂载或卸载

useEffect(() => {
  let isMouted = true;
  isUserAuthenticated(context.token)
    .then((val) => isMouted && setIsAuthenticated(val))
    .catch(setIsAuthenticated);
  return () => {
    isMouted = false;
  };
});

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM