1. 3
  1.  

  2. 1

    Here’s an example from this post:

    export default handleActions (
      {
        ADD_TODO: (state, action) => {
          return {
            ...state,
            currentTodo: '',
            todos: state.todos.concat (action.payload),
          };
        },
        LOAD_TODOS: (state, action) => {
          return {
            ...state,
            todos: action.payload,
          };
        },
        UPDATE_CURRENT: (state, action) => {
          return {
            ...state,
            currentTodo: action.payload,
          };
        },
        REPLACE_TODO: (state, action) => {
          return {
            ...state,
            todos: state.todos.map (
              t => (t.id === action.payload.id ? action.payload : t)
            ),
          };
        },
        REMOVE_TODO: (state, action) => {
          return {
            ...state,
            todos: state.todos.filter (t => t.id !== action.payload),
          };
        },
        [combineActions (SHOW_LOADER, HIDE_LOADER)]: (state, action) => {
          return {...state, isLoading: action.payload};
        },
      },
      initState
    );
    

    To me this and Redux in general both look like a half-baked reimplement of JavaScript’s class, except the method names are UPPER_CASE because they are “constant”, and state is managed elsewhere and passed as a parameter instead of being stored on this. Why reinvent this syntax? Is it just so we can be sure we’re using “functional programming” and have “no internal state”?

    If you want to eliminate boilerplate, cut the knot. Let me write my reducer as a class, and then generate the action creators, action names, etc from the class’s declared methods. You can still have all the benifits of redux while using a pleasing syntax.

    A sketch (pardon, on mobile):

    class TodoReducer extents Reducer {
      static initialState = () => ({ ... })
    
      addTodo(state, todo) {
        return {
          ...state,
          currentTodo: '',
          todos: state.todos.concat(todo),
        }
      }
    
      // more methods ...
    }
    
    // each camel-case methodName has a constant case METHOD_NAME.
    // Returns an object with each constant case method name mapped to itself. 
    export const actions = TodoReducer.actions()
    
    // creates an object mapping methodNames to functions that return an action object with type 
    // set to the constant case name of that method
    export const actionCreators = TodoReducer.actionCtreators
    
    // creates a reducer function from the class that handles dispatching redux actions as regular method calls on an instance of the class.
    // the wrapper function could also do hand-holding like assert there is no state being recorded in the instance
    // or, a new instance could be created for each dispatch, although the perf would suck
    export const reducer = TodoReducer.reducer()
    
    1. 1

      Personally I’d much rather avoid method-name magic, and I find splitting actions/constants/reducer into 3 flies in a module is an easy way to organise it, even if it is a fair chunk of boilerplate, and it all ends up seeming a lot simpler and less overblown than that example where it’s all done inline. Yes it could totally be more concise, but I really like explicit. Just my opinion, obvs.

      1. 1

        To me, the core benefits of redux are:

        1. All (important) state in one spot: simplifies reasoning and decision making
        2. Dependency injection: callers and consumers both have no knowledge of the state holder
        3. serializability: actions are simple objects that can be recorded, streamed across the network, replayed, ….

        What’s the purpose of the CONSTANTS file? How do you use those constants, other than to put them in the { type: } field of a serializable action, or to reason about an action in a reducer?

        How does moving the method names of a class into another file improve organization? What benefits do you see in an explicit composition from parts usually left implicit in other patterns?

        (As for the CONSTANT_NAME magic proposed in my example: sure, it’s immaterial. Trade following redux style convention for less magic.)

        1. 1

          I think those are the core benefits too :-) No purpose for such files other than to keep everything in one place, make it easily accessible, and know easily where I am with everything, and never have to remember name-conversion conventions or any of that stuff. Nothing more - but for my way of working it’s clearer and easier.

      2. 1

        One reason is that actions and reducers share a one to many relationship. You can decompose a single reducer into several reducers and potentially all of those reducers handle the same action. Actions are not meant to be remote function calls.