Swizec Teller - a geek with a hatswizec.com

Senior Mindset Book

Get promoted, earn a bigger salary, work for top companies

Senior Engineer Mindset cover
Learn more

    Update state during render, better explained

    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

    Published on July 7th, 2023 in Frontend, JavaScript, React, Reader Question

    Did you enjoy this article?

    Continue reading about Update state during render, better explained

    Semantically similar articles hand-picked by GPT-4

    Senior Mindset Book

    Get promoted, earn a bigger salary, work for top companies

    Learn more

    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 ❤️

    Created by Swizec with ❤️