How to access root state in a reducer in redux toolkit? - reactjs

I have a simple counter slice. In increment function I want to access root state. How can I do that?
const initialState = {
value: 1
}
export const counterSlice = createSlice({
name: "counter",
initialState,
reducers: {
increment: (state) => {
state.value++
}
}
})

Generally, you can't. Redux (and not only toolkit) is designed around the idea that your Reducer should only rely on it's own state value and the contents of the action coming in, nothing else.
If you really really need data from another state slice, you probably need to copy that into the action when calling it, but it is usually considered a bad idea.
You might have chosen your slice too small.

Related

Updating RTK (redux toolkit) state using other state values

Can I update the state based on other state variables (as shown below in setCurrentQuestion()), or will it cause unexpected issues?
I can think of two workarounds:
"external" helper function that calls a getCurrentQuestionIndex() selector passed into a getQuestionByIndex() selector which returns an IQuestion object that I pass into the setCurrentQuestion() reducer
Keep as is but use a draft safe selector instead of accessing the state object directly
const slice = createSlice({
name: 'blabla',
initialState: { questions: undefined, currentQuestion: undefined, noOfQuestions: 0, currentQuestionIndex: 0 } as QuizState,
reducers: {
setQuestions: (state, action: PayloadAction<IQuestion[]>) => {
state.questions = action.payload;
},
setCurrentQuestion: (state, action: PayloadAction<number>) => {
if (!state.questions) return
state.currentQuestion = state.questions[state.currentQuestionIndex]
}
},
state.currentQuestion should simply not exist in your state. You got state.questions as the source of truth, and state.currentQuestionIndex to remember which question is the current question. Anything further is not atomic and minimal anymore. It certainly works to have a state.currentQuestion which duplicates one of the items in state.questions, but then you end up with problems like this one. I'd recommend to add a selector getCurrentQuestion() that does the lookup and that can deal with state.questions being falsy. It also should never not be an array btw, it's easier to deal with state if the types don't change. I'd initialise it as an empty array, and then the selector could be:
const getCurrentQuestion = (state) => state.questions[state.currentQuestionIndex];
To solve what you had in mind, looking at a different part of the state before mutating it in the reducer, you could also use a thunk - they have getState() in the scope and can run conditional logic. So you could simply not dispatch setCurrentQuestion if state.questions is falsy. But a better solution imo is what I described above with the selector.

fundamental redux concept- how to actions launch reducers

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

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.

In Redux, is it necessary to do deep copy

The below object is action.data has a nested object address
{
name: 'Ben',
address: {
country: 'Australia',
state: 'NSW'
}
}
How should I handle it in the reducer?
const rootReducer = (state = initState, action) {
switch(action.type) {
switch RECEIVE_DATA:
return {...state, data: action.data}
}
}
Can I do it as above? that I just assign the whole object to data without copying?
or
const rootReducer = (state = initState, action) {
switch(action.type) {
switch RECEIVE_DATA:
const address = {...action.data.address}
const data = {...action.data, address}
return {...state, data}
}
}
Or should I do a deep copy of the object and assign it to data?
thanks
The "correct" way to handle updates of nested data is with multiple shallow copies, one for each level of nesting. It's also certainly okay to create a new object that just replaces one field completely, per your first example.
See the Redux docs section on Immutable Update Patterns for some info on how to properly do immutable updates, and also the Redux FAQ entry regarding deep cloning.
From Redux:
Common Redux misconception: you need to deeply clone the state. Reality: if something inside doesn't change, keep its reference the same!
No, shallow copy of Unchanged properties.Changed properties will be anyway new values, so no question of type of copy.
In code we achieve it like this return {...prevState}
If you are changing only one item in an array, Redux docs says you can use array.map, but if you know the index, this is faster:
state[action.args.index] = {
...state[action.args.index],
disregardLeafNode: action.args.checked
}
return state

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