Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reducer without switch #1167

Closed
mapreal19 opened this issue Dec 22, 2015 · 7 comments
Closed

Reducer without switch #1167

mapreal19 opened this issue Dec 22, 2015 · 7 comments

Comments

@mapreal19
Copy link

As reading from the docs you could avoid using switch with a function that maps action types to handlers. But still I'm not fully convinced with that approach.

In order to avoid those long switch statements, I suggest injecting the dependency of the reducers right into the actions.

The actions now will know how to change the state per each reducer. The reducer will just call them accordingly. This way, the reducer would follow the Open/Closed principle (Motivation by Uncle Bob: https://youtu.be/TMuno5RZNeE?t=3605)

I found this architecture much better. Take the case your system requires a new feature where 3 reducers listen to the same action. Then you would need to modify those reducers -- adding a new case on each switch statement.

Following the style I propose, you would only need to create the action with the reducers all in the same place. Example:

// ACTION
function addTodo() {
  reducers: {
    counterReducer: (state) => ...,
    todosReducer: (state) => ...,
    dummyReducer: (state) => ...
  }
}

// todos REDUCER
function todos(state = initialState, action) {
  if (action.reducers && typeof action.reducers.todosReducer === 'function') {
    return action.reducers.todosReducer(state);
  } else {
    return state;      
  }
}

Here you could see a refactor of the todomvc example using this idea.

Thoughts?

@imevro
Copy link
Contributor

imevro commented Dec 22, 2015

redux-actions and handleActions.

@gaearon
Copy link
Contributor

gaearon commented Dec 22, 2015

The whole point of Flux/Redux is to decouple actions and reducers. There may be many independent reducers handling one action, and there may be many independent actions handled by one reducer. This is what makes Flux/Redux scale because big teams can work on overlapping features without constant merge conflicts, as state mutation logic is kept separate even if it is caused by the same actions.

You also lose the ability to serialize and record/replay actions because they are not plain objects any more in your example. Enabling this was another big constraint of Redux. Please read this:

There are frameworks claiming to be similar to Flux, but without a concept of action objects. In terms of being predictable, this is a step backwards from Flux or Redux. If there are no serializable plain object actions, it is impossible to record and replay user sessions, or to implement hot reloading with time travel. If you’d rather modify data directly, you don’t need Redux.

@gaearon gaearon closed this as completed Dec 22, 2015
@gaearon
Copy link
Contributor

gaearon commented Dec 22, 2015

Think of action as a "message". The action doesn't know how the state changes. It's precisely reducers' job. Otherwise your reducers don't seem to contain code at all. Also don't forget that reducers can be composed further than a single level. What you propose doesn't work with reducer composition because you're effectively hardcoding reducer structure into the action objects.

@mapreal19
Copy link
Author

I see. Thank you for the quick feedback @gaearon 👍

@negamaxi
Copy link

Well, personally I hate switch so I use plain objects instead:

const actionHandlers = {
  [ADD_TODO] (state, todo) {
    // do stuff
  },
  [REMOVE_TODO] (state, id) {
    // do stuff
  }
}

const reducer =  (state = initialState, action) => {
    const { type, payload } = action
    const actionHandler = actionHandlers[type]
    if (actionHandler) {
      return actionHandler(state, payload)
    }
    return state
  }

@batcer
Copy link

batcer commented Nov 26, 2018

@negamaxi What does this syntax mean that you use?

[ADD_TODO] (state, todo) {
    // do stuff
  },

@negamaxi
Copy link

@batcer it's a way to create a method of a certain name stored in a constant:

const ADD_TODO = 'addTodo';

const handlers = {
  [ADD_TODO] () {}
}

...is the same as...

const handlers = {
  addTodo() {}
}

@reduxjs reduxjs locked as resolved and limited conversation to collaborators Nov 26, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants