34

In my Redux store I have multiple slices and I would like to access the lang state of the settingsSlice inside the speciesSlice.

Here is a code sample of my slices:

const settingsSlice = createSlice({
  name: 'settings',
  initialState: { lang: 'en', theme: 'dark' },
  reducers: {
    ...
  },
});

const speciesSlice = createSlice({
  name: 'species',
  initialState: data[`vds-list-${HERE I WANT TO USE THE "lang" STATE OF THE SETTINGSSLICE}`],
  reducers: {
    ...
  },
});

Thus far I haven't found a solution so maybe it's just not possible?

I could just use one slice with all of the state inside of there, but I'd really like to separate different parts of the state in different slices.

bignose
  • 30,281
  • 14
  • 77
  • 110
Jeremias
  • 343
  • 1
  • 3
  • 9
  • If you only use the settings default state then why not define `{ lang: 'en', theme: 'dark' }` as a constant and use that constant? – HMR Jun 18 '20 at 13:44
  • @HMR the state of the settingsSlice changes over time. It adapts to the users system language or the overridden setting. – Jeremias Jun 18 '20 at 14:00

5 Answers5

24

Reducers, by definition, only have access to the section of state that they own. So, if I have {users: usersReducer, posts: postsReducer}, the usersReducer has no access to the posts state slice at all.

See the Redux FAQ entry on "how can I share state between reducers?" for more details.

markerikson
  • 63,178
  • 10
  • 141
  • 157
10

I've managed to achieve this by simply importing the redux store object and have called getState() method.

so in speciesSlice reducer action you can do that:

const speciesSlice = createSlice({
  name: 'species',
  initialState: data[`vds-list-${HERE I WANT TO USE THE "lang" STATE OF THE SETTINGSSLICE}`],
  reducers: {
    ...
    setLang: (state, _) => {
      const reduxStore = store.getState();
      const lang = reduxStore.settingsSlice.lang;
      state = data[`vds-list-${lang}`];
    },
    ...
  },
});
Biskrem Muhammad
  • 4,074
  • 3
  • 31
  • 38
  • 7
    Would this be considered bad practice / anti pattern? – Dror Bar Jul 13 '22 at 11:31
  • @DrorBar No, I guess not, there are multiple mentions of the same methodology in redux documentations as well – Biskrem Muhammad Jul 15 '22 at 14:38
  • 3
    @Dror Personally I would consider it an anti-pattern to avoid. It seems like the reducer in this example doesn’t really do anything and can easily be replaced with a selector (that selects the active language and returns the corresponding data). – Linda Paiste Nov 11 '22 at 17:06
  • 1
    @BiskremMuhammad Could you cite/link to the part of the documentation where importing a store and working with it directly is mentioned – Gabriel Petersson Nov 27 '22 at 09:31
  • @GabrielPetersson.. here is the [api reference](https://redux.js.org/api/store#getState), see the examples. – Biskrem Muhammad Dec 05 '22 at 21:44
  • @BiskremMuhammad it's an example for how selectors work, nowhere do they recommend importing the store into your reducer https://redux.js.org/faq/reducers#how-do-i-share-state-between-two-reducers-do-i-have-to-use-combinereducers – Gabriel Petersson Dec 06 '22 at 13:16
  • 1
    This does not work, I get the error "You may not call store.getState() while the reducer is executing. The reducer has already received the state as an argument. Pass it down from the top reducer instead of reading it from the store." – edoedoedo Feb 15 '23 at 17:07
  • **You _cannot_ and _must not_ import the store into a reducer file, or try to call `store.getState()` in a reducer!** This is explicitly forbidden and will throw an error. – markerikson Mar 11 '23 at 20:50
2

As referenced in the Redux FAQ entry, "How do I share state between two reducers?", you can use redux-thunk to write a reducer that can access the entire state. For example:

const speciesSlice = createSlice({
  name: 'species',
  initialState: data[`vds-list-${HERE I WANT TO USE THE "lang" STATE OF THE SETTINGSSLICE}`],
  reducers: {
    ...
    internalSetLang: (state, action) => {
      state = data[`vds-list-${action.payload}`];
    },
    ...
  },
});

const { internalSetLang } = speciesSlice.actions;

export function setLang() {
  return (dispatch, getState) => {
    const lang = getState().settings.lang;
    dispatch(internalSetLang(lang));
  };
}

This is similar to @Biskrem Muhammad's answer but avoids having to use the Redux store as a global variable.

Josh Kelley
  • 56,064
  • 19
  • 146
  • 246
2

We can do it by adding extraReducers to respond to the action

// Slice A - sliceA.js
export const sliceAinitialState = {
  lang: 'en',
}
const sliceA = createSlice({
  name: 'A'
  initialSlice: sliceAinitialState,
  reducers: {
    langChangedAtSliceA: (state, action) => state.lang = action.payload,
  }
})
export const {langChangedAtSliceA } = sliceA.actions

// Slice B - sliceB.js
import {sliceAinitialState, langChangedAtSliceA} from 'sliceA.js'
const sliceB = createSlice({
  name: 'B'
  initialSlice: {
    lang: sliceAinitialState.lang
  },
  extraReducers: (builder) => {
    builder.addCase(langChangedAtSliceA, (state, action) => {
      state.lang = action.payload
    })
  }
})
Rustem
  • 46
  • 2
0

There is a specific createDraftSafeSelector function available in redux-toolkit for that usage.

challet
  • 870
  • 9
  • 21