Track React-Redux Redundant Re-Renders With “Why Did You Render” Version 3

@welldone-software/why-did-you-render@^3 tracks redundant Hooks re-renders + React-Redux@^7 uses hooks = Great way to track React-Redux redundant re-renders!

Vitali Zaidman
Apr 13 · 2 min read

Click here to read about Why Did You Render in general.

Since version 7, React-Redux uses React Hooks under the hood to subscribe to Redux state changes in React Context and trigger component re-renders when a state change occurs.

Since version 3, Why Did You Render tracks redundant React Hooks re-renders. You can read more about it here.

This means that the following simple code would result in tracking all React-Redux re-renders where the new props from connect are equal by value but different by reference- redundant re-renders:

whyDidYouRender(React, { include: [/^ConnectFunction$/] })

Here is how a notification of this type looks like:

Sadly WDYR triggers 3 notifications for every badly handled state change since React Redux uses 3 hooks under the hood.

Possible React-Redux Issues

Bad Action Handling

The following simple reducer and action would cause the state to be re-created on every action dispatched:

const initialState = {a: 'a'}function reducer(state = initialState, action){
return action.payload
}
store.dispatch({payload: {a: 'a'}})console.log(store.getState() === initialState)
// false

The reason is that the new state deep equals to the state before the dispatch but it doesn’t equals by reference.

To fix it, make sure you return the previous state if it deep equals to the new one:

const initialState = {a: 'a'}function reducer(state = initialState, action){
if(state.a === action.payload.a){
return state
}

return action.payload
}
store.dispatch({payload: {a: 'a'}})console.log(store.getState() === initialState)
// true

Bad Connection Handling

If the connect function creates an object with new props on it, a re-render would be triggered for every state change in any part of the application:

function SimpleComponent({c}){
console.log('render SimpleComponent!')
return <div>{c.a}</div>
}
const ConnectedComponent = connect(
// Notice how a new "c" is created here on every state change
// Even for state changes that has nothing to do with "a"
// Where state.a === prevState.a
state => ({c: {a: state.a})
)(SimpleComponent)
const initialA = store.getState().a// affects some completely different part of the redux state
store.dispatch({type: 'newX', payload: 'z'})
// "render SimpleComponent!" triggeredconsole.log(store.getState().a === initialA)
// true

To fix it, make sure to not create objects in connect:

function SimpleComponent({a}){
console.log('render SimpleComponent!')
return <div>{a}</div>
}
const ConnectedComponent = connect(
// Notice how the returned "a" prop is always just "a"
state => ({a: state.a})
)(SimpleComponent)
const initialA = store.getState().a// affects some completely different part of the redux state
store.dispatch({type: 'newX', payload: 'z'})
// NOT TRIGGERED: "render SimpleComponent!"console.log(store.getState().a === initialA)
// true

Welldone Software

The leading full-stack consultancy. Creating amazing frontends and rock-solid backends using top notch technologies and practices. Visit us at https://welldone.software.

Vitali Zaidman

Written by

https://twitter.com/vzaidman ⎝(•ω•)⎠ https://vzaidman.com

Welldone Software

The leading full-stack consultancy. Creating amazing frontends and rock-solid backends using top notch technologies and practices. Visit us at https://welldone.software.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade