Kyle Shevlin

Software Engineer
November 07, 2022
0 strokes bestowed

JSX was a Mistake

edit

It wasn't. I just wanted your attention. Thanks.

And now while I have it, I do want to discuss a bit of common confusion caused by JSX.

One of the most common misunderstandings I run into with people regarding React is this: If a component has a state change and rerenders, it does not cause children to rerender. I think this is very obvious when we remove JSX, and use React.createElement instead. Let's have a look.

Here we have one of my favorite components to demonstrate rerenders, a Box that changes background color whenever it's rendered. This Box allows us to pass children through it. Thus, we'll make an Example where we force an update, which will cause Box to rerender.

import { useForceUpdate } from './hooks'
import { randomRGB } from './utils'

function Box({ children }) {
  return (
    <div style={{ backgroundColor: randomRGB(), padding: '1rem' }}>
      {children}
    </div>
  )
}

function Example() {
  const forceUpdate = useForceUpdate()

  return (
    <Box>
      <button type="button" onClick={forceUpdate}>
        Update
      </button>
    </Box>
  )
}

As you can see, clicking "Update" causes the Box to change colors. This is because Box is a function that gets called when Example is called. This is obvious when instead of JSX, we use React.createElement instead.

function Box({ children }) {
  return React.createElement('div', {
    style: { backgroundColor: randomRGB(), padding: '1rem' },
    children,
  })
}

function Example() {
  const forceUpdate = useForceUpdate()

  return React.createElement(
    Box,
    null,
    React.createElement('button', { type: 'button', onClick: forceUpdate })
  )
}

Notice that this time, we replaced <div /> with React.createElement('div'). Then when we needed to render the Box in Example, we pass it to another call of React.createElement. Thus, each time Example rerenders, we call a function to render a Box and a function to render a button. Makes it pretty clear where functions are getting called.

How does this relate to children?

Well, simply put, children is typically not a function call, it's just a value.

What if I add a "slot" for children in our Example component like this:

function Example({ children }) {
  const forceUpdate = useForceUpdate()

  return React.createElement(
    Box,
    null,
    // createElement's third parameter is a rest parameter,
    // so we can add more items by adding more arguments
    React.createElement('button', { type: 'button', onClick: forceUpdate }),
    // here's the slot, it'll come after our button
    children
  )
}

Now, what happens if we pass Box as children of our Example?

<Example>
  <Box>I am a child</Box>
<Example>
I am a child

Notice that our child Box doesn't rerender, while the parent Box still does. It remains the same background color it was when it initially rendered, because it's not getting called again when Example rerenders.

Heck, let's have some fun and put Example into Example.

Kind of fun, isn't it?

When does this matter?

In most cases, I wouldn't fret about rerendering a Box or whatever component you might be working with, but it's just good knowledge to have. You'll be better off having this concept etched into your brain than not.

The place I'm most concerned about getting this right is the use of Context Providers. I wrote a very similar blog post all about that. Check it out if you're interested.

In general, if you have a component with frequent state changes and it uses children, consider letting as much of the inner contents be composed via children as possible to avoid unnecessary rendering.

You never know, you might just find your component is now a bit more reusable, too.


Finished reading?

Liked the post? Give the author a dopamine boost with a few "beard strokes". Click it up to 50 times show your appreciation.

Related Post:
Careful with Context Composition
Tags
React

Are you, or the company you work for, struggling with something mentioned in this article?
Would you benefit from a live training session?
Let's Talk
Kyle Shevlin's face, which is mostly a beard with eyes
Kyle Shevlin is a software engineer who specializes in JavaScript, TypeScript, React and frontend web development.

Let's talk some more about JavaScript, TypeScript, React, and software engineering.

I write a newsletter to share my thoughts and the projects I'm working on. I would love for you to join the conversation. You can unsubscribe at any time.

Data Structures and Algorithms Logo
Data Structures and Algorithms

Check out my courses!

Liked the post? You might like my courses, too. Click the button to view this course or go to Courses for more information.
I would like give thanks to those who have contributed fixes and updates to this blog. If you see something that needs some love, you can join them. This blog is open sourced at https://github.com/kyleshevlin/blog
alexloudenjacobwsmithbryndymentJacobMGEvanseclectic-codingjhsukgcreativeerikvorhesHaroenvmarktnoonandependabotmarcuslyonsbrentmclarkfederico-fiorinimedayzTowardsDeathFanchGadjonoahmateenbrandonpittmanmattridley
©2023 Kyle Shevlin. All Rights Reserved.