Skip to content

Instantly share code, notes, and snippets.

@kof
Last active January 18, 2020 09:06
Show Gist options
  • Save kof/9ead8b0899e2e1306311 to your computer and use it in GitHub Desktop.
Save kof/9ead8b0899e2e1306311 to your computer and use it in GitHub Desktop.
Action creator vs. reducer

When should you use action creator and when reducer?

Action creator

  • you need to have side effects
  • you need to read from store to decide what to do
  • you need to dispatch more than one action
  • action produced by action creator needs to contain all the data reducer can need to shape the components state

Reducer

  • should not have any side effects
  • should not read global application state
  • should not manipulate data structures passed with the action, it should only pick the right data and merge it into state
@kof
Copy link
Author

kof commented Jan 25, 2016

Also who defines how much should data passed from creator be normalized to fit the state needs?

@gaearon
Copy link

gaearon commented Feb 5, 2016

Yes, action creators knowing the state shape is generally an anti-pattern. It's fine if you normalize data from the server to some common shape and merge that into the state — but then you don't even need to react to different actions. Check out real-world example and its entities cache to get an idea of how to do that.

It all depends on where the source of truth is. If data from server is source of truth, just admit it by having an entities reducer that merges any new info right from the action. If you have complex local (or optimistic) mutations, this is the point where actions would be thin, and most of the logic would be inside the reducers.

@alfonsodev
Copy link

I stumbled upon this post because I found myself having all reducers responding with,

      return {...state, ...action.payload}

it felt fishy, I'd put it different words to see if I understood well why this is an anti-pattern
and to explain how I ( and I think many other users too) ended falling in the anti-pattern.

After reading this posts I understood that because
one action can affect one or many reducers its payload should be "reducer agnostic",
the reducer should be picking the properties it needs from the payload so that the reducer keeps
the ownership of the store shape.
This is much more maintainable because if you ever have to change the shape of the store,
there is only one place to change it, you go to the particular reducer and is just one change.
But If you do ...action.payload in the reducer, you'll have to check all the actions that you are reacting to,
change those, and then also check if there are other reducers reacting and adjust those too,
definitely much harder to maintain.

So why I ended doing return {...state, ...action.payload} ?
It started with a normal reducer like this ...

case UPDATE_USER_PROFILE:
  return {
    ...state,
    name: action.payload.name,
    address: action.payload.address,
    email: action.payload.email,
  }
break;

Because all keys match and action.payload looks redudant, and because we have es6 then I go ...

case UPDATE_USER_PROFILE:
  const { name, email, address } = action.payload
  return { ...state, name, email, address }
break;  

Which still kind of OK because it respects the patter, I'm cherry picking the properties from the payload,
but then you (or a colleague) come back, see that code, and realize the action just returns those 3 props,
and because we all love ...spreadOperator then I end up with

case UPDATE_USER_PROFILE:
  return { ...state, ...action.payload }
break;  

And now I learnt this ☝️ is an anti-pattern

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment