0

Run Code in React Before Render

 9 months ago
source link: https://daveceddia.com/react-before-render/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Want to run some code before your React component renders? There are a few ways to make this work, and we’ll talk about them here.

But, I have to warn you: Running code before render is usually a sign that you’re going against the grain of how React works.

TL;DR – There is no Before Render, only After

It makes perfect sense to think “I want to fetch data before my component renders”. Logical! But not how React works.

Here’s the thing:

React does not wait to render. Ever.

React will gladly kick off an asynchronous data fetch in the background, but then it will immediately proceed with rendering – whether the data has loaded or not. (and you can be almost certain that it will not have loaded yet)

There is no way to make it wait.

All is not lost, though. There’s an easy fix.

Components that render async data need to be prepared to render an empty state, at least once.

Think about what your app should look like before the data is ready. Maybe it’s empty, or maybe it’s a loading spinner, or some fancy skeleton state.

To embrace the way React works, kick off your data fetch after the first render, inside a useEffect block.

Just make sure to initialize the state to something that’s the same type as what it’ll eventually be!

Initialize State Before Render

Initializing state actually does run before the first render, and leaving it uninitialized is a common source of problems.

This leads to errors like Cannot read property 'map' of undefined' when the component tries to render before the data is ready.

If you have a call like useState() with nothing between the parens, that’s uninitialized (it’ll be undefined).

The rule of thumb is to initialize like-with-like: if the state will hold a string, initialize with a string. If it’s a number, init with a number. And so on.

Initialize Arrays

If you’re expecting an array from the server, initialize with an empty array.

const [items, setItems] = useState([]);

Initialize Objects

If you’re expecting an object, init with an object, or maybe null.

const [user, setUser] = useState(null);

Initialize State Lazily

If your init code has to do some heavy work, like mapping/filtering/reducing an array, you can wrap that initialization in a function and it will only run once:

const [products, setProducts] = useState(() => {
  return hugeListOfProducts.filter(isOnSale);
})

This is not a good place to fetch data or do anything asynchronous, though. Put async actions in a useEffect.

What Will Happen Before the Data is Ready?

Look through your code and make sure it won’t blow up if the data isn’t ready (if the value is null). Be especially careful if the data is initialized to or can become null or undefined!

return (
  <div>
    {user && user.name ? user.name : "Not loaded yet"}
  </div>
)

There are two new operators in ES2020 that can make this code simpler: optional chaining (?.) and nullish coalescing (??).

The optional chaining operator (?.) lets you safely access properties of an object that could be null.

return (
  <div>
    {user?.name || "Not loaded yet"}
  </div>
)

The nullish coalescing operator (??) returns the right-hand side when the left side is null or undefined. It’s useful in cases where you might normally use ||, like this:

return (
  <div>
    {user?.commentCount || "Not loaded yet"}
  </div>
)

This example has a bug – it will show “Not loaded yet” when commentCount is 0. Using the ?? operator instead of ||, it’ll work correctly:

return (
  <div>
    {user?.commentCount ?? "Not loaded yet"}
  </div>
)

?? works like the OR || operator, except that it doesn’t consider 0, '' or false to be falsy.

Fetch Data Before Render in the Parent

If you absolutely need to run some code before a component renders, then the solution is to avoid rendering that component at all, until you’re ready.

That means conditionally rendering it in the parent, which would look something like this. More detail in the comments:

function Child({ items }) {
  // Problem:
  // This will error if `items` is null/undefined
  return (
    <>
      {items.map(item => (
        <li key={item.id}>{item.name}</li>
      ))}
    </>
  );
}

function Parent() {
  // Uninitialized state will cause Child to error out
  const [items, setItems] = useState();

  // Data does't start loading
  // until *after* Parent is mounted
  useEffect(() => {
    fetch('/data')
      .then(res => res.json())
      .then(data => setItems(data));
  }, []);

  // Solution:
  // don't render Child until `items` is ready!
  return (
    <div>
      {items && <Child items={items}/>}
    </div>
  );
}

That’s It!

I hope that helps clear up some confusion around how to do things before mounting a React component. Just remember: There is no before, only after.

For a great deep dive into how React renders and re-renders, check out Mark Erikson’s guide to React rendering behavior.

Learn the basics of React in 5 days

Finally understand how React works! You will:

🎉 Get something on screen
💄 Write dynamic components
🏃 Make it interactive
😎 Fetch real data

🛳 Put it online

5 days, 5 emails. Walk away with the basics and a plan!

Get Lesson 1 right now 👇

Email Address
I respect your email privacy. Unsubscribe any time.

Learning React can be a struggle — so many libraries and tools!
My advice? Ignore all of them :)
For a step-by-step approach, check out my Pure React workshop.

Pure React plant

Learn to think in React

  • 90+ screencast lessons
  • Full transcripts and closed captions
  • All the code from the lessons
  • Developer interviews
Start learning Pure React now

Dave Ceddia’s Pure React is a work of enormous clarity and depth. Hats off. I'm a React trainer in London and would thoroughly recommend this to all front end devs wanting to upskill or consolidate.

Alan Lavender
Alan Lavender
@lavenderlens

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK