Why Did You Render Mr. Big Pure React Component Part 2- Common Fixing Scenarios

What to do with the many console logs you are probably about to receive when you start using @welldone-software/why-did-you-render?

Vitali Zaidman
Welldone Software
4 min readNov 19, 2018

--

In the previous article we introduced you to the @welldone-software/why-did-you-render library. Let’s look on some common scenarios you’ll encounter when using it:

Different Props

This notification is caused by re-rendering of a component when it’s props did not change but the props object did change.

Let’s take the following component for example:

const FatherComponent = props => (
<div>
<OtherComponent something={props.something}/>
<ClassDemo a={1}/>
</div>
)

If FatherComponent re-renders because props.something changed, ClassDemo would re-render as well. If this is a “heavy” component, you would want to prevent it’s re-rendering.

Because the props object is always new for each re-render of a child. This means, in our case, for ClassDemo:
prevProps !== nextProps, but:
prevProps.a === nextProps.a

Always remember: the <jsx prop='a'/> syntax is just a sugar for: React.createElement('jsx', {prop: 'a'}). This means whenever it’s father re-renders, jsx tries to update itself with a new props object.

This would trigger the following notification in the console:

To deal with it you can Make ClassDemo a pure component or to refactor the code to not trigger the update of FatherComponent somehow.

Different State

This scenario is exactly like the previous one but with states.

If the current state is: {stateKey: 'stateValue'} and you call:

this.setState({stateKey: 'stateValue'})

a render will occur although the actual state did not change:

prevState !== nextState, but:
prevState.stateKey === nextState.stateKey

To deal with it, make sure to not set state where it doesn’t have to change:

if(newStateKeyValue !== this.state.stateKey){
this.setState({{stateKey: newStateKeyValue}})
}

You can also make ClassDemo a pure component. But making components pure can be costly and not suggested in this case.

Props or State Equals by Value

If your state is {c: {d: 'd'}} and you call

this.setState({objectKey: {a: 'a'}})

a render will occur although the actual state did not change (by value):

prevState !== nextState, and
prevState.objectKey !== nextState.objectKey, but:
prevState.c.d === nextState.c.d

The same can happen with props if you render a component like this:

<ClassDemo a={{b: 'b'}}/>

prevProps !== nextProps, and prevProps.a !== nextProps.a, but:
prevProps.a.b === nextProps.a.b

Usually this is caused by creating a React component or a default value inside the render instead of creating it in advance. For example:

/// React Component Code ......render(){
return <ClassDemo
tooltip={<div>hi</div>}
// <jsx/> or ^^^^ React.createElement is always re-created
items={this.props.items || []}
// always new empty array ^^^
/>
}

Both can be solved by moving them outside of the render function:

const defaultItems = []
const tooltip = <div>hi</div>
/// React Component Code ......render(){
return <ClassDemo
tooltip={tooltip}
items={this.props.items || defaultItems}
/>
}

Children / React Elements

When we use JSX, we actually call React.createElement.

The reason why <SomeComponent/> !== <SomeComponent/> is that every reactElement created is a new immutable React Element.

This is true for children as well. Children are just props.

So what usually happens is that you want something to be pure but pass a new reactElement to it all the time:

<PureFather>
<SomeChild/>
</PureFather>

The result is the following error:

This is because it is equivalent to:

<PureFather children={<SomeChild/>}/>

And when react shouldComponentUpdate runs it re-renders since:

this.props.children !== nextProps.children

A good way to deal with it is to create a pure wrapper:

class PureFatherWrapper extends React.PureComponent{
render(){
return (
<PureFather>
<SomeChild/>
</PureFather>
)
}
}

This will ensure your father never re-renders.

React-Redux

The same scenario can be triggered by a React Redux store update when no update is actually needed:

function subStateReducer(state = defaultState, action){
switch(action.type){
case 'some-action':
return action.payload
default:
return state
}
}

if the state is state === {hello: {world: 'hi!'}

and you dispatch an action with the same payload as the state:
dispatch({action: 'some-action', payload: {hello: {world: 'hi!'}}).

Your connected connect(state => {hello: state.hello}) would trigger an update on the component with a different hello prop.

Equal Dates, Regular Expressions, React Components and Functions

All of these can trigger renders when their instance is recreated. For example:

<ClassDemo
regEx={/something/}
fn={function something(){}}
date={new Date('6/29/2011 4:52:48 PM UTC')}
reactElement={<div>hi!</div>}
/>

The library detects these and reports about them:

Enjoy Improving Your User’s Experience :D

--

--