はじめに
Cannot update a component () while rendering a different component ().
のエラーが出ました。
Cannot update a component (`Router`) while rendering a different component (`Private`). To locate the bad setState() call inside `Private`, follow the stack trace as described in https://react.dev/link/setstate-in-render
原因
レンダー中に別コンポーネントの状態変更が発生していることが原因でした。
以下参照です。
React コンポーネントは、レンダー中に他のコンポーネントに副作用を起こしてはいけません。
レンダー中に setState を呼び出すことはサポートされていますが同じコンポーネントに対してのみ可能です。
解決方法
該当部分にuseEffect
を使うことで、レンダリング後に副作用として実行するようにしました。
修正前
レンダー中にPrivate
コンポーネントでrouter.push
を実行してしまっています。
page.tsx
const Private = ({ children }: { children: React.ReactNode }) => {
const router = useRouter();
if (!loading) {
if (isSignedIn) {
return children;
} else {
//該当部分
router.push(`/signin`);
}
} else {
return <></>;
}
};
return (
<AuthContext.Provider
value={{ currentUser, setCurrentUser, isSignedIn, setIsSignedIn }}
>
<Private>
<p>Homeページです</p>
</Private>
</AuthContext.Provider>
);
修正後
レンダリング中に router.push
を実行しないように、useEffect
でラップしています。依存配列を明示的に指定し、不要なレンダーを防ぎます。
page.tsx
const Private = ({ children }: { children: React.ReactNode }) => {
const router = useRouter();
useEffect(() => {
if (!loading && !isSignedIn) {
router.push("/signin");
}
}, [loading, isSignedIn, router]); // 依存配列を明確にする
if (loading) {
return <></>; // ローディング中は何も表示しない
}
if (!isSignedIn) {
return null; // リダイレクト中はコンテンツをレンダリングしない
}
return <>{children}</>;
};
return (
<AuthContext.Provider
value={{ currentUser, setCurrentUser, isSignedIn, setIsSignedIn }}
>
<Private>
<p>Homeページです</p>
</Private>
</AuthContext.Provider>
);
下記も参考になります。
おわりに
エラーは解消しましたが、エフェクトの多用はアンチパターンとして取り上げられているので、使うべきかどうかは迷いどころです。
用法に注意したいところです。