CodinCat
CodinCat

Reputation: 15914

Extract one reducer out, without group other parts (a reducer that can handle "the rest states")

Let's say I want to have the following state structure:

{
  deep: { ... },
  foo: 0,
  bar: 0,
  str: ''
}

the deep may be complex, so I want to extract a dedicate reducer for it

const deepReducer = (state = {}, action) => { ... }

and for the rest part (foo, bar and str), just use another reducer for them because they are very simple

const initialState = { foo: 0, bar: 0, str: '' } // May also need to add deep here
const defaultReducer = (state = initialState, action) => { ... }

but this structure doesn't seem to work, if use combineReducers will group other reducers, then the structure will become:

{
  deep: { ... },
  others: {
    foo: 0,
    bar: 0,
    str: '',
  }
}

But I don't want to group them because their relationships are not that strong.

Is it possible to achieve this in 2 reducers? Or the only solution is to create a dedicate reducer for each state (foo, bar and str)?

Upvotes: 0

Views: 106

Answers (1)

therewillbecode
therewillbecode

Reputation: 7180

I feel like you should tackle the issue of state management first. Then it will be easier to split your reducers appropriately.

TLDR Keep your reducers as simple as possible and use a combination of state normalisation and selectors in order to ensure that you don't unnecessarily complicate your reducers.

State Management

As the docs state

The most common way to organize data within that top-level object is to further divide data into sub-trees, where each top-level key represents some "domain" or "slice" of related data.

First and foremost, it's important to understand that your entire application really only has one single reducer function

Every leaf (slice of data) in your state tree should have its own dedicated reducer. This is because each leaf represents a given domain of grouped data. Think of this state as a database. Each leaf would be representative of a table of grouped data.

State Normalisation

You say that you have a deeply nested object in your state.

 {
  deep: { ... },
  foo: 0,
  bar: 0,
  str: ''
}

Instead of worrying about how to structure your reducers around this state shape I would instead try and simplify your state tree in the first instance.

If you normalize your state this will reduce the level of nesting in your state objects. Therefore the reducers won't have to deal with deep levels of nesting and as a result will be simpler.

Remember that in coding simplicity is beauty.

Selectors

Your state contain the minimal representation of data in your application.

If despite your best efforts you have a deeply nested (complex) object in your state then you employ your selectors. If you don't use selectors then your reducers will need to know how to extract information from state. This will add uneccessary logic to the reducers and bloat them undermining our apps modularity.

By using selectors we can compute derived data from our state in a modular way as selectors can be reused and composed.

How do we use selectors?

Using the analogy of our state as a front end database. Selectors represent our queries that fetch data.

Selectors Example

This example from the docs uses the popular reselect library to create a selector. In this example the derived data which is being computed from our state is the todos which are actually visible. The filtered list is derived from our list of all todo items and the current visibility filter which could be set to all, none or enabled.

const getKeyword = (state) => state.keyword

const getVisibleTodosFilteredByKeyword = createSelector(
  [ getVisibleTodos, getKeyword ],
  (visibleTodos, keyword) => visibleTodos.filter(
    todo => todo.text.indexOf(keyword) > -1
  )
)

If we didn't perform this todo filtering operation then we would have to store the filtered todos in state and update the stae each time our visibility filter changed.

Upvotes: 1

Related Questions