1
votes

What happens to the state from redux when we trigger an action?

  1. We have the current state of redux
  2. We call the action
  3. Each reducer looks for its own case in the switch that contains the correct actionType
  4. Each reducer returns the state - does each reducer return a new reference to the state even in case of default return state?

What actually happens to the state if it is returned in reducer? Dose it use some kind of reduce function to compare state and call rerender, then return new reference and react then compare is there any change and invoke rerender?

1
I suggest you read a Redux tutorial such as the official one here. - Code-Apprentice
Or maybe I just don't understand your question. Will you edit it to be more specific? Adding a code example to illustrate what you are asking will help tremendously. - Code-Apprentice
this is a good tutorial daveceddia.com/redux-tutorial - Rmcr714
@Rmcr714 While that was a good tutorial at the time, Redux has since changed a lot and the tutorial has not been updated, so it's very outdated in terms of best practices and what we consider "modern" redux. I'd recommend to stick to the official tutorials. - phry

1 Answers

2
votes

What you are talking about here is a reducer which is made by combining multiple "slice" reducers through combineReducers. Something like this:

const rootReducer = combineReducers({
  users: usersReducer,
  posts: postsReducer,
})

Let's say that we dispatch an action with type ADD_USER -- this is something that usersReducer will respond to but the postsReducer won't.

Each reducer returns the state - does each reducer return a new reference to the state even in case of default return state?

The postsReducer doesn't care about this action and will just return state. This is a reference to the existing object. So state.posts is identical.

The usersReducer will create and return a new object for its state. So state.users is a reference to a different object in memory than it was before. If there are other arrays or objects nested inside of state.user then those objects will be the same references unless we replaced them in the reducer. So you are dealing with a "shallow copy" where state.users is a new object, but a deeply nested object like state.users.byId[5] might be the same exact object as before.


You might think that the root state would always be a new object reference when dealing with combineReducers since it is essentially doing this:

const rootReducer = (state, action) => ({
  users: usersReducer(state, action),
  posts: postsReducer(state, action),
});

But the actual implementation is more complex and it involves some checks to prevent unnecessary object creation. If every individual reducer just uses default: return state; then the original state object is returned.

Here's how the combineReducers function works:

  • It has a boolean flag property hasChanged which is initially false.
  • It creates a nextState which is initially an empty object (so obviously that's a new object).
  • It goes through each of the individual property reducers and updates the nextState with the updated state for that property. If the property reducer returned a new state then hasChanged becomes true.
  • If hasChanged is still false after all the property reducers have been called, then it ignores the nextState and instead returns the original state variable.
  • Otherwise it returns the nextState.

When using the React bindings like useSelector, it (by default) does a strict equality check on the returned value. So if you are returning a new object which is an exact copy of the previous object, it will trigger a re-render because the object is a different reference. This is why it is important to use something like reselect to memoize your selectors if they involve creating an object through array.map, array.filter, etc. Otherwise you will have a new object reference every single time. useSelector also supports custom equality checks so that you have control over when re-renders occur.