fundamental redux concept- how to actions launch reducers - reactjs

to make things simple consider these two reducers(written in typescript):
export type userActionTypes =
| Interface1
| Interface2
const initialState1 = {...//some state} //<--comment indicates some sort of values are present
const initialState2 = {...//some state}
const reducer1 = (state = initialState1, action: userActionTypes){
switch(action.type) {
case action1.case1:
return {...//some new state1}
default: return state
}
}
const reducer2 = (state = initialState2, action: userActionTypes){
switch(action.type){
case action2.case2:
return {...//some new state1}
default: return state
}
}
const rootReducer = combineReducers({
mReducer1: reducer1,
mReducer2: reducer2
})
now say somewhere we call the following in our code:
newAction = {type: action2.type2, //some other values}
dispatch(newAction);
**my question is: ** How does react know which reducer to call? I mean, it doesn't pass the action to every reducer there is in the rootReducer, does it? I mean if that was the case, then all the default cases would be meaningless and all the cases in the case statements would have to be unique. That is not possible, is it?
How does react know which reducer to call?

How does react know which reducer to call? I mean, it doesn't pass the
action to every reducer there is in the rootReducer, does it?
Trick question, redux actually calls all of your reducers.
Consider your root reducer:
const rootReducer = combineReducers({
mReducer1: reducer1,
mReducer2: reducer2
})
This creates a reducer function tree. When an action is dispatched to the store it calls the root reducer and passes the current state and the current action. The reducers in turn recursively call their nested combined reducers, passing state and action until they reach a leaf where you hit a reducer function and compute their next state. They either have a case to handle action or the return the default case which is simply their current state. The recursion goes back up, returning each next state slice piece, combined at each level until you arrive back at the root reducer which returns the entire next state object.
I mean if that was the case, then all the default cases would be
meaningless and all the cases in the case statements would have to be
unique. That is not possible, is it?
Remember that each reducer function is operating on only its little slice of state, not the entire state object. The default case is there for the reducer to return its current state value since there is no work for it to do. All the reducer cases within a reducer function should be unique. If two actions trigger the same state update then they should be grouped
case "case1":
case "case13":
// both cases apply the same update

Related

Where should I put a common code in Redux?

I have an application with Reactjs and Redux. There is an action which resets the state of the reducers. My question is: where is the best place to perform that? I am considering two options:
each reducer handle the action and resets its state
const reducer1 = (state = defaultState, action) => {
switch (action.type) {
case 'reset': {
// ...
}
// ...
}
the root reducer resets the global state
const appReducer = combineReducers({
reducer1,
reducer2,
reducer3
})
const rootReducer = (state, action) => {
if ( (action.type === 'reset') ) {
state = {}
}
return appReducer(state, action)
}
The best practice is to have an action for reseting each reducer, this helps for extensibility in the future. Dispatch the clearState action, but do not set an empty object. Set it to the initial state, because if you put an empty object you can introduce bugs
The first thing we must have clear is that reducers don't have states, stores have states, so you shouldn't say "the state of the reducer".
A reducer is a function that performs some change in the state of a store, and there is no specific limitation in the scope of such change, besides the scope of the store, so many reducers have overlapping scopes over the state of the store.
After that, I see no reason why you can not reset the whole state of the store with a single reducer, and when you need to make other changes with different scope, you can create other reducers to manage it.

How reducers update the store

I am new to Redux and i am finding some problems with understanding concept of reducers,
I can see many examples showing it takes current state and return updated state,
My question is how it updates the Store by returning the new state ( i am finding difficult to understand the mechanism ),
can some one please explain me .
The Redux store is nothing but just an object holding all states of the application. The reducer is the only way to update the store.
The reducer is a pure function with takes an old state and returns a new state. In reducer what we need to do is that we just provide the old state which is store currently having and then the new state which we are going to change state. You can refer this for detailed explanation for reduce function.
In simple words, reducer takes existing state object updates some property passed through reducer function and returns new object state.
Following link has better explanation. This is very nice blog how to create your own redux. You will get exactly what happens in the redux store.
https://www.jamasoftware.com/blog/lets-write-redux/
this is image that i found very helpfull when i was learning the same concept.
Dispatch
When you dispatch any function it goes to all reducers and if the type of dispatch matches it will change the state of that reducer.
functionName:()=>(dispatch)({type:'some-thing-to-match',payload})
Reducers
That handle state change.
Store
Combination of all the reducers (root reducer).
const store = combineReducers({
Reducer1:r1,
Reducer2:r2,
Reducer3:r3
})
For example take the dispatch function in TodoList that matches in r1 and changes its state.Then by connect from 'react-redux' we will connect that reducers state to TodoList.
var mapStateToProps = state=>{
return:{
r1:r1
}
}
then react will react to any change in state. If state of r1 is changed then it will update that component.
Your question how it update store by returning state. Your reducer will get store(state) and function as input and change the store according to function and return the state to store.
Then we can connect our component to that store to catch any change in it.
As we can see in image. Dispatch will change the store's state.Then
you can import(connect) that reducer to see the changes in your
component.(here TodoItem is that component)
Actually this is the part that i was missing about reducers,The part i didnt catch was reducers out put has to be assigned to store property
let Action={type:'SET_VISIBILITY_FILTER',text: 'test pay load'}
//Invoking Reducer
let store=todoApp({},Action)
//Reducer
function todoApp(state = initialState, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return Object.assign({}, state, {
message: action.text
})
default:
return state
}
}

Redux, when to split reducers?

I have a Redux store with a reducer consisting of 9 case statements. Six of these cases deal with populating an array in the state white three deal with sorting said array, thus there is no natural way of splitting the reducer, is it ok to leave the reducer as one?
The number of case statements isn't the right metric for deciding whether you should split your reducer. Rather, it's how deeply nested is the state slice that your reducer action handler is working on. If its too deep, the code could explode.
Lets say you have this state shape:
{
cart: {
products: [ {name:"shirt"}, {name:"pants"} ],
checkout: false,
}
}
In a single/global reducer setup, you might have this action handler:
//reducer.js
case ADD_CART_PRODUCT:
const {newProduct} = payload
const {cart: {products}} = state
return {...state, cart: {...state.cart, products: [...products, newProduct] } }
Not bad, but you can split off a cartReducer for action handlers that affects the cart state slice, shrinking your code to this:
//cartReducer.js
case ADD_CART_PRODUCT:
const {newProduct} = payload
const {products} = state //this is actually state.cart from the global perspective
return {...state, products: [...products, newProduct] } }
Yes, it is okay to leave the reducer as one. Lets say if I have a reducer for User, I would consider splitting the reducer if it got too big to manage or if it had nothing to do with User and that was my first one. You only have 9 switch cases and that should be fine.

Selector being called even when I don't mutate what its mapPropsToState

I have a React app that does some simple recording. I have a Component Recorder which connects to my redux store like this:
export default connect(
state => ({
recordings: state.recordings,
recordingSelector: selectRecordingBufferWithID(this.recordingID)
}),
dispatch =>
bindActionCreators({
startNewRecordingAction,
stopNewRecordingAction
},
dispatch
)
)(SampleRecorder);
The problem I'm having is that selectRecordingBufferWithID in my redux code is firing too often. Part of my reducer code looks like this:
function samplesReducer(state = [], action) {
switch (action.type) {
case MORE_SAMPLES:
return [...action.samples];
default:
return state
}
}
function recordingsReducer(state = [], action) {
switch (action.type) {
case NEW_RECORDING:
return newRecording(state, action.recordingID);
case STOP_RECORDING:
return stopRecording(state, action.recordingID);
default:
return state
}
}
const rootReducer = combineReducers({
samplesReducer,
recordingsReducer
})
const store = createStore(rootReducer);
export { store };
So, while I want selectRecordingBufferWithID to be utilized only when a START/STOP_RECORDING action occurs, it is called for each time MORE_SAMPLES is called.
My understanding of react-redux is that the selector is part of the mapStateToProps function that the connect function accepts. And somehow, connect cause my component to render and for its props to be updated with the mapped state from the redux store. the selectRecordingBufferWithID selector will also be called each time this happens so I can do a refined getter into the store.
So to summarize, my recordingSelector is firing more often than I expect. My only theory is that my reducers are somehow mutating the state of state.recordings each time it tries to reduce state.samples which makes react-redux render my component with it mapped to state.recording.
But otherwise, I'm stuck.
connect does not work the way you think it does. What it really does is:
Subscribe to the store. This subscription will be triggered after every dispatched action.
Execute your mapStateToProps to inject the initial set of props to your Sample Recorder component.
When any action dispatches, the subscription kicks in, and connect applies again your mapStateToProps to new global state.
If your selector returns the same props as before, it won't render your SampleRecorder again.
So the misunderstanding is that your selector shouldn't be called. But the fact is that connect needs to call your selector to decide when to re-render and when not.
The summary of this is that your selector should be either simple, or memoizable using reselect to avoid expensive calculations. You didn't show you selector code so we can't tell from here. :)

Combine Reducers without their functional keys

I have been working on a React / Redux Application, where now the reducer for single feature has become big enough for the thought of dividing the reducer further.
So I was thinking is there a way to divide the reducer into two different reducers and combining them without adding keys of their functional names.
For Ex:
const nowShowing = function(state = {}, action){
switch(action.type){
case types.NS_MOVIES:
return Object.assign({}, state, { nowShowingMovies: action.data })
}
const comingSoon = function(state = {}, action){
switch(action.type){
case types.CS_MOVIES:
return Object.assign({}, state, { comingSoonMovies: action.data })
}
I am looking to check if its possible to combine these reducers
const movies = combineReducers({
nowShowing,
comingSoon
})
in such a way that i can access them in the following manner
mapStateToProps(state){
nowShowing: state.movies.nowShowing,
comingSoon: state.movies.comingSoon
}
The reason i am looking for a solution like this is because, it will allow my entire application to work as earlier, but the nowShowing & comingSoon reducer become separate and my code becomes more modular.
I did try returning an object instead of combining the reducer using the combineReducers function. But that causes the entire state of the reducer to reset when there is a change in value as it recreates the object.
I think this kind of solution is not possible or it should not be attempted. But I would to understand more why, as I do agree that this points to more over architectural flaw in constructing the reducers in the first place.
Any help would be highly appreciated.
Thanks

Resources