question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Suggestion: add syntax or examples for nested slices

See original GitHub issue

Currently redux-toolkit does not provide (or I couldn’t find) an integrated way to define nested slices/reducers.

To split some slice logic into smaller slices, I’m currently doing something like this:

import { createSlice } from '@reduxjs/toolkit'

// import the reducer, initial state and relevant actions from the desired slice
import otherReducer, {
  complexAction,
  initialState as otherReducerInitialState
} from './otherReducer'

const myReducer = createSlice({
  name: 'myReducer',
  initialState: {
    someData: 'hello',
    // manually bind initial state
    someComplexData: otherReducerInitialState
  },
  reducers: {
    doStuff: (state, action) => {
      // ...
    },
  },
  extraReducers: {
    // manually bind each action
    [complexAction]: (state, action) => {
      // manually foward the state and action to the nested reducer
      state.someComplexData = otherReducer(state.someComplexData, action)
    },
  }
})

export const { doStuff } = myReducer.actions
export default myReducer.reducer

The above example works, but it’s a lot of boilerplate code, and I can’t shake the felling there should be a better way to describe nested slices.

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:25
  • Comments:13

github_iconTop GitHub Comments

5reactions
jessepinhocommented, May 12, 2021

I made a combineSlices helper function to make it easy to adapt @agrinko’s suggestion to any use case (note that it requires that you install reduce-reducers):

import { combineReducers } from "@reduxjs/toolkit";
import reduceReducers from "reduce-reducers";

/**
 * Combines a slice with any number of child slices.
 *
 * This solution is inspired by [this GitHub
 * comment](https://github.com/reduxjs/redux-toolkit/issues/259#issuecomment-604496169).
 *
 * @param sliceReducer - The reducer of the parent slice. For example, if your
 * slice is called `mySlice`, you should pass in `mySlice.reducer`.
 * @param {object} sliceInitialState - The initial state object passed to
 * `createSlice`. This is needed so that we know what keys are in the initial
 * state.
 * @param {object} childSliceReducers - An object of child slice reducers, keyed
 * by the name you want them to have in the Redux state namespace. For example,
 * if you've imported child slices called `childSlice1` and `childSlice2`, you
 * should pass in this argument as `{ childSlice1: childSlice1.reducer,
 * childSlice2: childSlice2.reducer }`. NOTE: The name of each child slice
 * should reflect its place in the namespace hierarchy to ensure that action
 * names are properly namespaced. For example, the `name` property of the
 * `childSlice1` slice should be `mySlice/childSlice1`, not just `childSlice1`.
 */
const combineSlices = (sliceReducer, sliceInitialState, childSliceReducers) => {
  const noopReducersFromInitialState = Object.keys(sliceInitialState).reduce(
    (prev, curr) => {
      return {
        ...prev,
        [curr]: (s = null) => s,
      };
    },
    {}
  );

  const childReducers = combineReducers({
    ...childSliceReducers,
    ...noopReducersFromInitialState,
  });

  return reduceReducers(sliceReducer, childReducers);
};

export default combineSlices;

Note that you need to pass it the initial state from the parent slice so that it knows what keys to make dummy no-op reducers for. Thus, here’s an example of how it could be used:

import mySlice2 from "./mySlice2"
import mySlice3 from "./mySlice3"
import combineSlices from "./combineSlices"

// Note that `initialState` is defined as a variable before being passed
// to `createSlice` since it will need to also be passed to `combineSlices`.
const initialState = {
  count: 0,
}

const mySlice = createSlice({
  name: "mySlice",
  initialState,
  reducers: {
    setCount(state, action) {
      state.count = action.payload
    },
  }
})

export default combineSlices(mySlice.reducer, initialState, {
  mySlice2: mySlice2.reducer,
  mySlice3: mySlice3.reducer,
})
5reactions
markeriksoncommented, Mar 26, 2020

Yeah, combineReducers is opinionated that way - it assumes all fields get handled by individual slice reducers, which is really more meant for top-level chunks of state vs individual fields.

We’ve had requests to remove that warning, but opted not to do so. Note that there’s a million re-implementations of combineReducers out there, so it’s easy to use one that doesn’t have that warning if you’d like.

Read more comments on GitHub >

github_iconTop Results From Across the Web

How do I update a nested Item in a different reducer (slice ...
You want to iterate over every scene and remove the deleted role from the roles array.
Read more >
Usage Guide - Redux Toolkit
In this example, both users and posts would be considered "slices". Both of the reducers: "Own" a piece of state, including what the...
Read more >
2359-subslice-pattern-syntax - The Rust RFC Book
Permit matching sub-slices and sub-arrays with the syntax .. . Binding a variable to the expression matched by a subslice pattern can be...
Read more >
Practical Redux, Part 11: Nested Data and Trees
This time, we're going to add creation of new entities, implement loading of nested relational data, and display that nested data in a...
Read more >
Python - Slicing Strings - W3Schools
You can return a range of characters by using the slice syntax. Specify the start index and the end index, separated by a...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found