A few readers wrote in to say that the React Can Update State During Render email last week made no sense. Here's a better explanation.
Now, I tried to write a better example that more closely matches wtf we were doing in our production code that made this trick important. It didn't work. Both versions exhibit the same bug. (╯°□°)╯︵ ┻━┻
It worked IRL, I promise. This solved a huge problem for us.
Here's the example, maybe you can spot what I'm doing wrong:
https://codesandbox.io/p/sandbox/update-state-during-render-vs-useeffect-rrjwzj
And, again, the more toyful example from last week that shows this pattern working as expected:
The background
We're using react-hook-form at work because it's an awesome library. Based on uncontrolled form inputs, which leads to better UI performance.
Uncontrolled inputs create a few sharp edges when you need React app state and your form state to match. One such example are default values that come from an API.
Normally, you set default values like this:
useForm({
defaultValues: {
test_field: "default value",
},
})
That doesn't work when you don't know the values on first render. You can't update them later because then they're not default anymore.
How then do you make this work?
const defaultValue = useSlowValue()
;<SmartInput name="test_field" defaultValue={defaultValue} />
useEffect approach
Your natural reaction may be to reach for an effect. You want to do something when a prop changes.
const SmartInput = (props: { name: string; defaultValue: string }) => {
const { register, setValue } = useFormContext()
useEffect(() => {
setValue(props.name, props.defaultValue)
}, [props.defaultValue])
return <input {...register(props.name)} />
}
When prop changes, set value on the field.
This may lead to a double UI update when the prop changes. You re-render when the prop changes, then run the effect, then re-render again.
As your app grows and you have more and more of these effects, things start getting weird and difficult to debug.
Update state during render approach
You can avoid weird bugs from effects building up by adopting the update state during render pattern.
const BetterSmartInput = (props: { name: string; defaultValue: string }) => {
const { register, setValue } = useFormContext()
const [prevDefaultValue, setPrevDefaultValue] = useState(props.defaultValue)
if (prevDefaultValue !== props.defaultValue) {
setValue(props.name, props.defaultValue)
setPrevDefaultValue(props.defaultValue)
}
return <input {...register(props.name)} />
}
When React re-renders your component, see if the prop value changed from before and update. This lets React abandon the in-progress render and start again with correct state.
No double UI updates. More predictable behavior as your app grows.
Cheers,
~Swizec
PS: I can just tell there's an engineering lesson hiding in how my smol isolated example of a neat trick didn't work, but the same thing worked in a large app
Continue reading about Update state during render, better explained
Semantically similar articles hand-picked by GPT-4
- React can update state during render
- Why react-hook-form is my new favorite form library
- Fixing laggy UI with React.memo
- Delightful state management with hooks and Constate
- How to use React Context effectively
Learned something new? Want to become a React expert?
Learning from tutorials is easy. You follow some steps, learn a smol lesson, and feel like you got dis 💪
Then comes the interview, a real world problem, or a question from the boss. Your mind goes blank. Shit, how does this work again ...
Happens to everyone. Building is harder than recognizing and the real world is a mess! Nothing fits in neat little boxes you learned about in tutorials.
That's where my emails come in – lessons from experience. Building production software :)
Leave your email and get the React email Series - a series of curated essays on building with React. Borne from experience, seasoned with over 12+ years of hands-on practice building the engineering side of growing SaaS companies.
Get Curated React Essays
Get a series of curated essays on React. Lessons and insights from building software for production. No bullshit.
Have a burning question that you think I can answer? Hit me up on twitter and I'll do my best.
Who am I and who do I help? I'm Swizec Teller and I turn coders into engineers with "Raw and honest from the heart!" writing. No bullshit. Real insights into the career and skills of a modern software engineer.
Want to become a true senior engineer? Take ownership, have autonomy, and be a force multiplier on your team. The Senior Engineer Mindset ebook can help 👉 swizec.com/senior-mindset. These are the shifts in mindset that unlocked my career.
Curious about Serverless and the modern backend? Check out Serverless Handbook, for frontend engineers 👉 ServerlessHandbook.dev
Want to Stop copy pasting D3 examples and create data visualizations of your own? Learn how to build scalable dataviz React components your whole team can understand with React for Data Visualization
Want to get my best emails on JavaScript, React, Serverless, Fullstack Web, or Indie Hacking? Check out swizec.com/collections
Did someone amazing share this letter with you? Wonderful! You can sign up for my weekly letters for software engineers on their path to greatness, here: swizec.com/blog
Want to brush up on your modern JavaScript syntax? Check out my interactive cheatsheet: es6cheatsheet.com
By the way, just in case no one has told you it yet today: I love and appreciate you for who you are ❤️