Search code examples
javascriptreactjsreduxreact-redux

How to share state between reducers in redux (how to organize redux)?


I learn react with redux and can't understand how can I use state properties inside the reducers I have created reducers file inside /reducers folder

reducersText.js

export const textReducer = (state = [], action) => {
 switch (action.type) {
     case 'ADD_TEXT':
         return state = action.payload;
     default:
         return state;
 }
}
export const addCustomMinusWordReducer = (state = [], action) => {
 switch (action.type) {
     case 'ADD_CUSTOM_MINUS_WORD':
         return state = action.payload;
     default:
         return state;
 }
}

and I create index.js inside /reducers folder

import {textReducer, addCustomMinusWordReducer} from "./text"
import {combineReducers} from "redux"

const allReducers = combineReducers({
    text: textReducer,
    customMinusWords: addCustomMinusWordReducer
})

export default allReducers

My problem is I can not access the text state which created in textReducer inside addCustomMinusWordReducer. I found related link, but this is not what I am looking for. How can I use state in reducers? Thanks!


Solution

  • From the official documentation of Redux:

    Many users later want to try to share data between two reducers, but find that combineReducers does not allow them to do so. There are several approaches that can be used:

    • If a reducer needs to know data from another slice of state, the state tree shape may need to be reorganized so that a single reducer is handling more of the data.

    • You may need to write some custom functions for handling some of these actions. This may require replacing combineReducers with your own top-level reducer function. You can also use a utility such as reduce-reducers to run combineReducers to handle most actions, but also run a more specialized reducer for specific actions that cross state slices.

    • Async action creators such as redux-thunk have access to the entire state through getState(). An action creator can retrieve additional data from the state and put it in an action, so that each reducer has enough information to update its own state slice.

    So, By exactly using your approach without using redux-thunk and without reordering the state with parent reducers. What you can do is pass in the state from action creators.

    For example; One action creator would be:

     const ExampleComponent = ({ text, clicked }) => {
        return (
           <button onClick={() => clicked(text)} />
        );
     };
    
     export default connect(state => ({ 
       text: state.text 
     }, dispatch => ({
       clicked: (textState) => dispatch({ type: 'ADD_CUSTOM_MINUS_WORD', textState })
     })(ExampleComponent);
    

    And then in reducer you can simply do;

    export const addCustomMinusWordReducer = (state = [], action) => {
     // action.textState will contain the data from textState
     // do something with action.textState
     const { textState } = action;
    }
    

    If you would actually want to use global state inside reducers just as the documentation suggests easy way would be to use a utility such as reduce-reducers. Instead of using combineReducers you could then use reduceReducers. And in the reducer the state object will contain global state instead of local state that combineReducer would give.

    PS:

    As the previous answer suggested it is a bad idea to change state inside reducers which might cause hard to fix bugs in application.

    so, Instead of state = state.payload better idea would be to use spread operator like state = { ...state, ...state.payload } which would then append changes to state rather than directly setting it.