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
Welldone Software

--

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.

It is possible to track the React-Redux’s useSelctor hook since version v4 which in a much more convenient way to achieve the same.
Read about version 4 including tracking the “useSelector” hook here

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

--

--