React Hooks触发‘Too many re-renders’错误的代码重构方案咨询
Hey David, let's break down what's causing that frustrating loop and fix it with useEffect—exactly what you suspected you needed!
The Root of the Problem
Right now, you're calling setText() directly in the component's render body. Every time your component renders (which happens after any state update), it checks if error or data exists and updates the text state. That state update triggers another render, which checks error/data again, updates state again, and so on—creating an infinite loop of re-renders.
The Fix: Use useEffect for Side Effects
useEffect lets you run code after render, and only when specific dependencies change. We'll use it to update text only when data or error from useQuery changes, breaking the loop.
Here's the refactored code:
const EmailVerification = () => { const [showMessage, setShowMessage] = useState(true); const [text, setText] = useState("...Loading. Do not close."); const { data, error } = useQuery(VERIFY_EMAIL); useEffect(() => { // Update text only when data or error changes if (error) { setText(genericErrorMessage); } else if (data) { setText(emailVerificationMessage); } // Dependencies array: run this effect only when these values change }, [data, error, genericErrorMessage, emailVerificationMessage]); return ( <Wrapper> <Message setShowMessage={setShowMessage} text={text} /> </Wrapper> ) }
Why This Works
- The
useEffectcallback runs after the component renders, not during render. - The dependencies array (
[data, error, ...]) tells React to only re-run this effect when those values change. So whenuseQueryfinishes fetching and updatesdataorerror, the effect runs once to updatetext—no infinite loops. - We added
genericErrorMessageandemailVerificationMessageto the dependencies to avoid stale closures (if these values ever change, the effect will pick up the latest version).
Bonus: Simplify with Conditional Rendering (Optional)
If you don't need text as a separate state (since it's derived from data/error), you could even skip the state entirely and compute the text directly in the render:
const EmailVerification = () => { const [showMessage, setShowMessage] = useState(true); const { data, error } = useQuery(VERIFY_EMAIL); const text = error ? genericErrorMessage : data ? emailVerificationMessage : "...Loading. Do not close."; return ( <Wrapper> <Message setShowMessage={setShowMessage} text={text} /> </Wrapper> ) }
This avoids state updates entirely, which is even cleaner if your text doesn't need to be stateful. Either approach will fix the re-render loop!
内容的提问来源于stack exchange,提问作者David

