簡體   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