Rerender Problems in React Continued…

Shallow Waters
Photo by Sven van der Pluijm on Unsplash

Follow Along / Test the Examples

Take the following example.

Check out the Repo

You can can fork and clone this repo to follow along, or follow the same example below.

Example

import { useState, useEffect } from 'react';
import ChildComponent from './components/ChildComponent';
const initialArray = [
{number: 1},
{number: 2},
{number: 3},
{number: 4},
{number: 5}
]
;
const App = () => { const [myArray, setMyArray] = useState([]) useEffect(() => { setMyArray(initialArray) }, []) const reverseArray = () => {
const reversedArray = myArray.reverse()
setMyArray(reversedArray)
}
return(
<>
<button onClick={reverseArray}>
Reverse Order
</button>
<ChildComponent arrayData={myArray} />
</>)
}
export default App;
  1. We set up the piece of state called myArray and updated it after the component mounts (note that the second argument in the useEffect hook is “[]”) with a value of initialArray. The initialArray value is—aptly named—an array, and is full of objects: initialArray = [ {number: 1}, {number: 2}, {number: 3}, {number: 4}, {number: 5} ]
  2. We created a button that will fire the function called reverseArray.
  3. We defined the reverseArray function to take the state of myArray, reverse it, and then set the myArray value to that reversed order. So, when clicked the first time, it should reverse the order of the array to [ {number: 5}, {number: 4}, {number: 3}, {number: 2}, {number: 1} ].
  4. We have passed the myArray piece of state down as a prop called arrayData in the ChildComponent.

Will the Child Component Rerender when we reverse the order of myArray?

Photo by Daniel Herron on Unsplash

React and Referential Equality: Object.is

React uses the Object.is when checking the objects in the array in this example. The comparison made is shallow, looking to see if the object admits of “referential equality” to the previous state’s object. (You can check out more on this issue here and here.)

A Quick Fix

Did you notice anything fishy about how we reset state above (reproduced here):

const reverseArray = () => {
const reversedArray = myArray.reverse()
setMyArray(reversedArray)
}
  1. Open the application in the browser with npm start.
  2. Open up the devtools console.
  3. Click the “Reverse Order” button.
Screenshot demonstrating that myArray is mutated when reverseArray function is called.

COPY AND THEN MUTATE to Avoid Pass-By-Reference Side-Effects

As a good rule-of-thumb, it’s better to first copy an array before exerting any mutations on the data, so that you avoid any side-effects.

const reverseArray = () => {
const reversedArray = [...myArray].reverse()
setMyArray(reversedArray)
}
Screenshot demonstrating that the copied array is not identical with the original array and that the original array is not changed with the copy.

The new state passed as a prop to the ChildComponent will cause a rerender of the ChildComponent.

Conclusion

There’s really no “trick” at play here. Instead, we are following a programming principle that we should have noticed we were violating from the beginning:

Avoid side effects when working with JavaScript objects, such as arrays, by first creating a copy of that object.

The unintentional mutation of the original array, myArray is a side effect. Following the principle of avoiding side effects such as this can help us avoid a problem of React ignoring a change in our state and props object due to referential equality.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
David Ryan Morphew

David Ryan Morphew

I’m very excited to start a new career in Software Engineering. I love the languages, frameworks, and libraries I’ve already learned / worked with (Ruby, Rails,