Redux: Recommendations for best practices regarding action-creators, reducers, and selectors

Created on 22 Dec 2015  Ā·  106Comments  Ā·  Source: reduxjs/redux

My team has been using Redux for a couple of months now. Along the way I've occasionally found myself thinking about a feature and wondering "does this belong in an action-creator or a reducer?". The documentation seems a bit vague on this fact. (Or perhaps I've just missed where it's covered, in which case I apologize.) But as I've written more code and more tests I've come to have stronger opinions about where things _should_ be and I thought it would be worth sharing and discussing with others.

So here are my thoughts.

Use selectors everywhere

This first one is not strictly related to Redux but I'll share it anyway since it's indirectly mentioned below. My team uses rackt/reselect. We typically define a file that exports selectors for a given node of our state tree (eg. MyPageSelectors). Our "smart" containers then use those selectors to parameterize our "dumb" components.

Over time we've realized that there is added benefit to using these same selectors in other places (not just in the context of reselect). For example, we use them in automated tests. We also use them in thunks returned by action-creators (more below).

So my first recommendation is- use shared selectors _everywhere_- even when synchronously accessing data (eg. prefer myValueSelector(state) over state.myValue). This reduces the likelihood of mistyped variables that lead to subtle undefined values, it simplifies changes to the structure of your store, etc.

Do _more_ in action-creators and _less_ in reducers

I think this one is very important although it may not be immediately obvious. Business logic belongs in action-creators. Reducers should be stupid and simple. In many individual cases it does not matter- but consistency is good and so it's best to _consistently_ do this. There are a couple of reasons why:

  1. Action-creators can be asynchronous through the use of middleware like redux-thunk. Since your application will often require asynchronous updates to your store- some "business logic" will end up in your actions.
  2. Action-creators (more accurately the thunks they return) can use shared selectors because they have access to the complete state. Reducers cannot because they only have access to their node.
  3. Using redux-thunk, a single action-creator can dispatch multiple actions- which makes complicated state updates simpler and encourages better code reuse.

Imagine your state has metadata related to a list of items. Each time an item is modified, added to, or removed from the list- the metadata needs to be updated. The "business logic" for keeping the list and its metadata in sync could live in a few places:

  1. In the reducers. Each reducer (add, edit, remove) is responsible for updating the list _as well as_ the metadata.
  2. In the views (container/component). Each view that invokes an action (add, edit, remove) it is also responsible for invoking an updateMetadata action. This approach is terrible for (hopefully) obvious reasons.
  3. In the action-creators. Each action-creator (add, edit, remove) returns a thunk that dispatches an action to update the list and then another action to updates the metadata.

Given the above choices, option 3 is solidly better. Both options 1 and 3 support clean code sharing but only option 3 supports the case where list and/or metadata updates might be asynchronous. (For example maybe it relies on a web worker.)

Write "ducks" tests that focus on Actions and Selectors

The most efficient way to tests actions, reducers, and selectors is to follow the "ducks" approach when writing tests. This means you should write one set of tests that cover a given set of actions, reducers, and selectors rather than 3 sets of tests that focus on each individually. This more accurately simulates what happens in your real application and it provides the most bang for the buck.

Breaking it down further I've found that it's useful to write tests that focus on action-creators and then verify the outcome using selectors. (Don't directly test reducers.) What matters is that a given action results in the state you expect. Verifying this outcome using your (shared) selectors is a way of covering all three in a single pass.

discussion

Most helpful comment

@dtinth @denis-sokolov I agree with you too on that. Btw when I was referencing the redux-saga project I maybe not made it clear that I'm against the idea of making the actionCreators grow and be more and more complex over time.

The Redux-saga project is also an attempt to do what you are describing @dtinth but there is a subtile difference with what you both say. It seems you want to say if you write every raw event that happened to the action log then you can easily compute any state from the reducers of this action log. This is absolutly true, and I've taken that path for a while until my app became very hard to maintain because the action log became not explicit, and reducers too complex over time.

Maybe you can look at this point of the original discussion that lead to Redux saga discussion: https://github.com/paldepind/functional-frontend-architecture/issues/20#issuecomment-162822909

Usecase to solve

Imagine you have a Todo app, with the obvious TodoCreated event. Then we ask you to code an app onboarding. Once the user creates a todo, we should congratulate him with a popup.

The "impure" way:

This is what @bvaughn seems to prefer

function createTodo(todo) {
   return (dispatch, getState) => {
       dispatch({type: "TodoCreated",payload: todo});
       if ( getState().isOnboarding ) {
         dispatch({type: "ShowOnboardingTodoCreateCongratulation"});
       }
   }
}

I don't like this approach because it makes the action creator highly coupled to the app view's layout. It assumes the actionCreator should know the structure of the UI state tree to take its decision.

The "compute everything from raw events" way:

This is what @denis-sokolov @dtinth seems to prefer:

function onboardingTodoCreateCongratulationReducer(state = defaultState, action) {
  var isOnboarding = isOnboardingReducer(state.isOnboarding,action);
  switch (action) {
    case "TodoCreated": 
        return {isOnboarding: isOnboarding, isCongratulationDisplayed: isOnboarding}
    default: 
        return {isOnboarding: isOnboarding, isCongratulationDisplayed: false}
  }
}

Yes you can create a reducer that knows if the congratulation should be displayed. But then you have a popup that will be displayed without even an action saying that the popup has been displayed. In my own experience doing that (and still have legacy code doing that) it is always better to make it very explicit: NEVER display the congratulation popup if no action DISPLAY_CONGRATULATION is fired. Explicit is much easier to maintain than implicit.

The simplified saga way.

The redux-saga uses generators and may look a bit complicated if you are not used to but basically with a simplified implementation you would write something like:

function createTodo(todo) {
   return (dispatch, getState) => {
       dispatch({type: "TodoCreated",payload: todo});
   }
}

function onboardingSaga(state, action, actionCreators) {
  switch (action) {
    case "OnboardingStarted": 
        return {onboarding: true, ...state};
    case "OnboardingStarted": 
        return {onboarding: false, ...state};
    case "TodoCreated": 
        if ( state.onboarding ) dispatch({type: "ShowOnboardingTodoCreateCongratulation"});
        return state;
    default: 
        return state;
  }
}

The saga is a stateful actor that receive events and may produce effects. Here it is implemented as an impure reducer to give you an idea of what it is but it actually is not in redux-saga project.

Complicating a bit the rules:

If you take care of the initial rule it is not very explicit about everything.
If you look at the above implementations, you will notice that the congratulation popup opens everytime we create a todo during the onboarding. Most likely, we want it to open only for the first created todo that happens during the onboarding and not all of them. Also, we want to allow the user to eventually redo the onboarding from the beginning.

Can you see how the code would become messy in all 3 implementations over time as the onboarding becomes more and more complicated?

The redux-saga way

With redux-saga and the above onboarding rules, you would write something like

function* onboarding() {
  while ( true ) {
    take(ONBOARDING_STARTED)
    take(TODO_CREATED)
    put(SHOW_TODO_CREATION_CONGRATULATION)
    take(ONBOARDING_ENDED)
  }
}

I think it solves this usecase in a much simpler way than the above solutions. If I'm wrong please give me your simpler implementation :)

You talked about impure code, and in this case there is no impurity in the Redux-saga implementation because the take/put effects are actually data. When take() is called it does not execute, it returns a descriptor of the effect to execute, and at some point an interpreter kicks in, so you don't need any mock to test the sagas. If you are a functional dev doing Haskell think Free / IO monads.


In this case it permits to:

  • Avoid complicating actionCreator and make it depends on getState
  • Make the implicit more explicit
  • Avoid coupling transversal logic (like the onboarding above) to your core business domain (creating todo)

It can also provide an interpretation layer, permitting to translate raw events into more meaningful/high-level events (a bit like ELM does by wrapping events as they bubble up).

Examples:

  • "TIMELINE_SCROLLED_NEAR-BOTTOM" could lead to "NEXT_PAGE_LOADED"
  • "REQUEST_FAILED" could lead to "USER_DISCONNECTED" if error code is 401.
  • "HASHTAG_FILTER_ADDED" could lead to "CONTENT_RELOADED"

If you want to achieve a modular app layout with ducks, it can permit to avoid coupling ducks together. The saga becomes the coupling point. The ducks just have to know of their raw events, and the saga interpret these raw events. This is far better than having duck1 dispatching directly actions of duck2 because it makes duck1 project more easy to reuse in another context. One could however argue that the coupling point could also be in actionCreators and this is what most people are doing today.

All 106 comments

Curious if you use Immutable.js or other. In the handful of redux things I've built I couldn't imagine not using immutable, but I _do_ have a pretty deeply nested structure that Immutable helps tame.

Wow. What an oversight for me _not_ to mention that. Yes! We use Immutable! As you say, it's hard to imagine _not_ using it for anything substantial.

@bvaughn One area I've struggled with is where to draw the line between Immutable and the components. Passing immutable objects into the Components lets you use pure-render decorators/mixins very easily but then you end up with IMmutable code in your components (which I don't like). So far I have just caved and done that but I suspect you use selectors in the render() methods instead of directly accessing Immutable.js' methods?

To be honest this is something we haven't defined a hard policy on yet. Often we use selectors in our "smart" containers to extra native values from our immutable objects and then pass the native values to our componentsĀ as strings, booleans, etc. Occasionally we'll pass an Immutable object but when we do- we almost always pass a Record type so that the component can treat it like a native object (with getters).

I've been moving in the opposite direction, making action creators more trivial. But I'm really just starting out with redux. Some questions about your approach:

1) How do you test your action creators? I like moving as much logic as possible to pure, synchronous functions that don't depend on external services because its easier to test.
2) Do you use the time travel with hot reloading? One of the neat things with with react redux devtools is that when hot reloading is setup, the store will rerun all of the actions against the new reducer. If I were to move my logic into the action creators, I'd lose that.
3) If your action creators dispatch multiple times to bring about an effect, does that mean that your state is briefly in an invalid state? (I'm thinking here of multiple synchronous dispatches, not ones dispatch asynchronously at a later point)

Use selectors everywhere

Yes that seems like saying your reducers are an implementation detail of your state and that you expose your state to your component through a query API.
Like any interface it permits to decouple and make it easy to refactor the state.

Use ImmutableJS

IMO with new JS syntax it's not so much useful to use ImmutableJS anymore as you can easily modify lists and objects with normal JS. Unless you have very large lists and objects with lots of properties and you need structural sharing for performance reasons ImmutableJS is not a strict requirement.

Do more in actionCreators

@bvaughn you should really look at this project: https://github.com/yelouafi/redux-saga
When I started discussing about sagas (initially backend concept) to @yelouafi it was to solve this kind of problem. In my case I first tried to use sagas while plugging in an user onboarding on an existing app.

1) How do you test your action creators? I like moving as much logic as possible to pure, synchronous functions that don't depend on external services because its easier to test.

I tried to describe this above, but basically... I think it makes the most sense (to me so far) to test your action-creators with a "ducks"-like approach. Begin a test by dispatching the result of an action-creator and then verify the state using selectors. This way- with a single test you can cover the action-creator, its reducer(s), and all related selectors.

2) Do you use the time travel with hot reloading? One of the neat things with with react redux devtools is that when hot reloading is setup, the store will rerun all of the actions against the new reducer. If I were to move my logic into the action creators, I'd lose that.

No, we don't use time-travel. But why would your business logic being in an action-creator have any impact here? The only thing that updates your application's state is your reducers. And so re-running the created actions would achieve the same result either way.

3) If your action creators dispatch multiple times to bring about an effect, does that mean that your state is briefly in an invalid state? (I'm thinking here of multiple synchronous dispatches, not ones dispatch asynchronously at a later point)

Transient invalid state is something you can't really avoid in some cases. So long as there is eventual consistency then it's usually not a problem. And again, your state could be temporarily invalid regardless of your business logic being in the action-creators or the reducers. It has more to do with side-effects and the specifics of your store.

IMO with new JS syntax it's not so much useful to use ImmutableJS anymore as you can easily modify lists and objects with normal JS. Unless you have very large lists and objects with lots of properties and you need structural sharing for performance reasons ImmutableJS is not a strict requirement.

The primary reasons for using Immutable (in my eyes) aren't performance or syntactic sugar for updates. The primary reason is that it prevents you (or someone else) from _accidentally_ mutating your incoming state within a reducer. That's a no-no and it's unfortunately easy to do with plain JS objects.

@bvaughn you should really look at this project: https://github.com/yelouafi/redux-saga
When I started discussing about sagas (initially backend concept) to @yelouafi it was to solve this kind of problem. In my case I first tried to use sagas while plugging in an user onboarding on an existing app.

I have actually checked out that project before :) Although I haven't yet used it. It does look neat.

I tried to describe this above, but basically... I think it makes the most sense (to me so far) to test your action-creators with a "ducks"-like approach. Begin a test by dispatching the result of an action-creator and then verify the state using selectors. This way- with a single test you can cover the action-creator, its reducer(s), and all related selectors.

Sorry, I got that part. What I was wondering about is the part of tests that interacts with the asynchronicity. I might write a test something like this:

var store = createStore();
store.dispatch(actions.startRequest());
store.dispatch(actions.requestResponseReceived({...});
strictEqual(isLoaded(store.getState());

But what does your test look like? Something like this?

var mock = mockFetch();
store.dispatch(actions.request());
mock.expect("/api/foo.bar").andRespond("{status: OK}");
strictEqual(isLoaded(store.getState());

No, we don't use time-travel. But why would your business logic being in an action-creator have any impact here? The only thing that updates your application's state is your reducers. And so re-running the created actions would achieve the same result either way.

What if the code is changed? If I change the reducer, the same actions are replayed but with the new reducer. Whereas if I change the action creator, that the new versions aren't replayed. So to consider two scenarios:

With a reducer:

1) I try an action in my app.
2) There is a bug in my reducer, resulting in the wrong state.
3) I fix the bug in the reducer and save
4) Time travel loads the new reducer and puts me in the state I should have been in.

Whereas with an action creator

1) I try an action in my app.
2) There is a bug in the action creator, resulting in the wrong action being created
3) I fix the bug in the action creator and save
4) I'm still in the incorrect state, which will require me to at least try the action again, and possibly refresh if it put me in a completely broken state.

Transient invalid state is something you can't really avoid in some cases. So long as there is eventual consistency then it's usually not a problem. And again, your state could be temporarily invalid regardless of your business logic being in the action-creators or the reducers. It has more to do with side-effects and the specifics of your store.

I guess my way of thinking of redux insists that the store is always in a valid state. The reducer always takes a valid state and produces a valid state. What cases do you think forces one to allow some inconsistent states?

Sorry for interruption, but what do you mean by invalid and valid states
here? Data being loaded or action being executed but not yet finished look
like a valid transient state to me.

What do you mean by transient state in Redux @bvaughn and @sompylasar ? Weither the dispatch finishes, or it throws. If it throws then the state do not change.

Unless your reducer has code issues, Redux only has states that are consistent with the reducer logic. Somehow all actions dispatched are handled in a transaction: weither the whole tree updates, or the state does not change at all.

If the whole tree updates but not in an appropriate way (like a state that React can't render), it's just you don't have done your job correctly :)

In Redux the current state is to consider that a single dispatch is a transaction boundary.

However I understand the concern of @winstonewert that seems to want to dispatch 2 actions synchronously in a same transaction. Because sometimes actionCreators dispatch multiple actions and expect that all the actions are executed correctly. If 2 actions are dispatched and then the second one fail, then only the 1st one will be applied, leading to a state that we could consider "inconsistent". Maybe @winstonewert wants that if the 2nd action dispatch failes, then we rollback the 2 actions.

@winstonewert I've implemented something like that in our internal framework here and it works fine until now: https://github.com/stample/atom-react/blob/master/src/atom/atom.js
I also wanted to handle rendering errors: if a state can't be rendered successfully I wanted my state to be rollbacked to avoid blocking the UI. Unfortunatly until next release React does a very bad job when the render methods throw errors so it was not that much useful but may be in the future.

I'm pretty sure that we can allow a store to accept multiple sync dispatches in a transaction with a middleware.

However I'm not sure it would be possible to rollback the state in case of rendering error, as generally the redux store has already "commited" when we try to render its state. In my framework there is a "beforeTransactionCommit" hook that I use to trigger the rendering and to eventually rollback on any render error.

@gaearon I wonder if you plan to support these kind of features and if it would be possible with the current API.

It seems to me that redux-batched-subscribe does not permit to do real transaction but just reduce the number of renderings. What I see is that the store "commit" after each dispatch even if the subscription listener is only fired once at the end

Why do we need complete transaction support? I don't think I understand the use case.

@gaearon I'm not really sure yet but would be happy to know more of @winstonewert usecase.

The idea is that you could do dispatch([a1,a2]) and if a2 fails, then we rollback to the state before a1 was dispatched.

In the past I've often been dispatching multiple actions synchronously (on a single onClick listener for example, or in an actionCreator) and primarily implemented transactions as a way to call render only at the end of all actions being dispatched, but this has been solved in a different way by the redux-batched-subscribe project.

In my usecases the actions I used to fire on a transaction was mostly to avoid unnecessary renderings, but the actions did make sense independently so even if the dispatch failed for the 2nd action, not rollbacking the 1st action would still give me a consistent state (but maybe not the one that was planned...). I don't really know if someone can come up with a usecase where a full rollback would be useful

However when the rendering fails doesn't it make sense to try to rollback to the last state for which the render does not fail instead of trying to make progress on an unrenderable state?

Would a simple reducer enhancer work? e.g.

const enhanceReducerWithTheAbilityToConsumeMultipleActions = (reducer =>
  (state, actions) => (typeof actions.reduce === 'function'
    ? actions.reduce(reducer, state)
    : reducer(state, actions)
  )
)

With that, you can dispatch an array to the store. The enhancer unpacks individual action and feeds it to each reducer.

Ohh @gaearon I did not know that. I did not notice there were 2 distinct projects that try to solve a quite similar usecase in different ways:

Both will permit to avoid unnecessary renderings, but the 1st one would rollback all the batched actions while the 2nd one would only not apply the failing action.

@gaearon Ouch, my bad for not looking at that. :flushed:


Action Creators represent Impure Code

I probably havenā€™t had as much hands-on experience with Redux as most people, but at first sight, I have to disagree with ā€œdo more in action creators and do less in reducers,ā€ Iā€™ve had some similar discussion inside our company.

In Hacker Way: Rethinking Web App Development at Facebook where the Flux pattern is introduced, the very problem that leads to Flux being invented is imperative code.

In this case, an Action Creator that does I/O is that imperative code.

Weā€™re not using Redux at work, but at where I work we used to have fine grained actions (that, of course, all make sense on its own) and trigger them in a batch. For example, when you click on a message, three actions are triggered: OPEN_MESSAGE_VIEW, FETCH_MESSAGE, MARK_NOTIFICATION_AS_READ.

Then it turns out that these ā€œlow-levelā€ actions are no more than a ā€œcommandā€ or ā€œsetterā€ or ā€œmessageā€ to set some value inside a store. We might as well go back and use MVC and end up with simpler code if we keep doing it like this.

In a sense, Action Creators represent impure code, while Reducers (and Selectors) represent pure code. Haskell people have figured out that itā€™s better to have less impure code and more pure code.

For instance, in my side project (using Redux), I use webkitā€™s speech recognition API. It emits onresult event as you speak. There are two choices ā€” where do these events get processed?

  • Have the action creator process the event first, then send it into the store.
  • Just send the event object into the store.

I went with number two: Just send the raw event object into the store.

However, it seems that Redux dev-tools doesnā€™t like it when non-plain objects are sent into the store, so I added some tiny logic in the action creator to transform these event objects into plain objects. (The code in the action creator is so trivial that it canā€™t go wrong.)

Then the reducer can combine these very primitive events and build up the transcript of whatā€™s spoken. Because that logic lives inside pure code, I can very easily tweak it live (by hot-reloading the reducer).

Iā€™d like to support @dtinth. Actions should represent events that happened from the real world, not how we want to react to these events. In particular, see CQRS: we want to log as much detail about a real life events, and likely the reducers will be improved in the future and process old events with new logic.

@dtinth @denis-sokolov I agree with you too on that. Btw when I was referencing the redux-saga project I maybe not made it clear that I'm against the idea of making the actionCreators grow and be more and more complex over time.

The Redux-saga project is also an attempt to do what you are describing @dtinth but there is a subtile difference with what you both say. It seems you want to say if you write every raw event that happened to the action log then you can easily compute any state from the reducers of this action log. This is absolutly true, and I've taken that path for a while until my app became very hard to maintain because the action log became not explicit, and reducers too complex over time.

Maybe you can look at this point of the original discussion that lead to Redux saga discussion: https://github.com/paldepind/functional-frontend-architecture/issues/20#issuecomment-162822909

Usecase to solve

Imagine you have a Todo app, with the obvious TodoCreated event. Then we ask you to code an app onboarding. Once the user creates a todo, we should congratulate him with a popup.

The "impure" way:

This is what @bvaughn seems to prefer

function createTodo(todo) {
   return (dispatch, getState) => {
       dispatch({type: "TodoCreated",payload: todo});
       if ( getState().isOnboarding ) {
         dispatch({type: "ShowOnboardingTodoCreateCongratulation"});
       }
   }
}

I don't like this approach because it makes the action creator highly coupled to the app view's layout. It assumes the actionCreator should know the structure of the UI state tree to take its decision.

The "compute everything from raw events" way:

This is what @denis-sokolov @dtinth seems to prefer:

function onboardingTodoCreateCongratulationReducer(state = defaultState, action) {
  var isOnboarding = isOnboardingReducer(state.isOnboarding,action);
  switch (action) {
    case "TodoCreated": 
        return {isOnboarding: isOnboarding, isCongratulationDisplayed: isOnboarding}
    default: 
        return {isOnboarding: isOnboarding, isCongratulationDisplayed: false}
  }
}

Yes you can create a reducer that knows if the congratulation should be displayed. But then you have a popup that will be displayed without even an action saying that the popup has been displayed. In my own experience doing that (and still have legacy code doing that) it is always better to make it very explicit: NEVER display the congratulation popup if no action DISPLAY_CONGRATULATION is fired. Explicit is much easier to maintain than implicit.

The simplified saga way.

The redux-saga uses generators and may look a bit complicated if you are not used to but basically with a simplified implementation you would write something like:

function createTodo(todo) {
   return (dispatch, getState) => {
       dispatch({type: "TodoCreated",payload: todo});
   }
}

function onboardingSaga(state, action, actionCreators) {
  switch (action) {
    case "OnboardingStarted": 
        return {onboarding: true, ...state};
    case "OnboardingStarted": 
        return {onboarding: false, ...state};
    case "TodoCreated": 
        if ( state.onboarding ) dispatch({type: "ShowOnboardingTodoCreateCongratulation"});
        return state;
    default: 
        return state;
  }
}

The saga is a stateful actor that receive events and may produce effects. Here it is implemented as an impure reducer to give you an idea of what it is but it actually is not in redux-saga project.

Complicating a bit the rules:

If you take care of the initial rule it is not very explicit about everything.
If you look at the above implementations, you will notice that the congratulation popup opens everytime we create a todo during the onboarding. Most likely, we want it to open only for the first created todo that happens during the onboarding and not all of them. Also, we want to allow the user to eventually redo the onboarding from the beginning.

Can you see how the code would become messy in all 3 implementations over time as the onboarding becomes more and more complicated?

The redux-saga way

With redux-saga and the above onboarding rules, you would write something like

function* onboarding() {
  while ( true ) {
    take(ONBOARDING_STARTED)
    take(TODO_CREATED)
    put(SHOW_TODO_CREATION_CONGRATULATION)
    take(ONBOARDING_ENDED)
  }
}

I think it solves this usecase in a much simpler way than the above solutions. If I'm wrong please give me your simpler implementation :)

You talked about impure code, and in this case there is no impurity in the Redux-saga implementation because the take/put effects are actually data. When take() is called it does not execute, it returns a descriptor of the effect to execute, and at some point an interpreter kicks in, so you don't need any mock to test the sagas. If you are a functional dev doing Haskell think Free / IO monads.


In this case it permits to:

  • Avoid complicating actionCreator and make it depends on getState
  • Make the implicit more explicit
  • Avoid coupling transversal logic (like the onboarding above) to your core business domain (creating todo)

It can also provide an interpretation layer, permitting to translate raw events into more meaningful/high-level events (a bit like ELM does by wrapping events as they bubble up).

Examples:

  • "TIMELINE_SCROLLED_NEAR-BOTTOM" could lead to "NEXT_PAGE_LOADED"
  • "REQUEST_FAILED" could lead to "USER_DISCONNECTED" if error code is 401.
  • "HASHTAG_FILTER_ADDED" could lead to "CONTENT_RELOADED"

If you want to achieve a modular app layout with ducks, it can permit to avoid coupling ducks together. The saga becomes the coupling point. The ducks just have to know of their raw events, and the saga interpret these raw events. This is far better than having duck1 dispatching directly actions of duck2 because it makes duck1 project more easy to reuse in another context. One could however argue that the coupling point could also be in actionCreators and this is what most people are doing today.

@slorber This is an excellent example! Thanks for taking the time to explain the benefits and drawbacks of each approach clearly. (I even think that should go in the docs.)

I used to explore a similar idea (which I named ā€œworker componentsā€). Basically, itā€™s a React component that doesnā€™t render anything (render: () => null), but listens to events (e.g. from stores) and triggers other side-effects. That worker component is then put inside the application root component. Just another crazy way of handling complex side effects. :stuck_out_tongue:

Lots of discussion here while I was sleeping.

@winstonewert, you raise a good point about time-travel and replaying of buggy code. I think certain types of bugs/changes won't work with time-travel either way, but I think overall you're right.

@dtinth, I'm sorry, but I'm not following along with most of your comment. Some part of your action-creator/reducer "ducks" code has to be impure, in that some part of it has to fetch data. Beyond that you lost me. One of the primary purposes of my initial post was just one of pragmatism.


@winstonewert said, "I guess my way of thinking of redux insists that the store is always in a valid state."
@slorber asked, "What do you mean by transient state in Redux @bvaughn and @sompylasar ? Weither the dispatch finishes, or it throws. If it throws then the state do not change."

I'm pretty sure we're thinking about different things. When I said "transient invalid state" I was referring to a use-case like the following. For example, myself and a colleague recently released redux-search. This search middleware listens for changes to collections of searchable things and then (re-)indexes them for search. If the user supplies filter text, redux-search returns the list of resource uids that match the user's text. So consider the following:

Imagine your application store contains a few searchable objects: [{id: 1, name: "Alex"}, {id: 2, name: "Brian"}, {id: 3, name: "Charles"}]. The user has entered filter text "e" and so the search middleware contains an array of ids 1 and 3. Now imagine that user 1 (Alex) is deleted- either in response to a user-action locally or a refresh of remote data that no longer contains that user record. At the point when your reducer updates the users collection, your store will be temporarily invalid- because redux-search will reference an id that no longer exists in the collection. Once the middleware has run again it will correct the invalid state. This sort of thing can happen anytime one node of your tree is related to another node.


@slorber said, "I don't like this approach because it makes the action creator highly coupled to the app view's layout. It assumes the actionCreator should know the structure of the UI state tree to take its decision."

I don't understand what you mean by the approach coupling the action-creator "to the app view's layout". The state tree _drives_ (or informs) the UI. That's one of the major purposes of Flux. And your action-creators and reducers are, by definition, coupled with that state (but not with the UI).

For what it's worth the example code you wrote as something I prefer is not the sort of thing I had in mind. Maybe I did a poor job of explaining myself. I think a difficulty in discussing something like this is that it typically doesn't manifest in simple or common examples. (For example standard the TODO MVC app is not complex enough for nuanced discussions like this.)

Edited for clarity on the last point.

Btw @slorber here is an example of what I had in mind. It's a bit contrived.

Let's say your state has many nodes. One of those nodes stores shared resources. (By "shared" I mean resources that cached locally and accessed by multiple pages within your application.) These shared resources have their own action-creators and reducers ("ducks"). Another node stores information for a particular application page. Your page also has its own duck.

Let's say your page needed to load the latest and greatest Thing and then allow a user to edit it. Here's an example action-creator approach that I might use for such a situation:

import { fetchThing, thingSelector } from 'resources/thing/duck'
import { showError } from 'messages/duck'

export function fetchAndProcessThing ({ params }): Object {
  const { id } = params
  return async ({ dispatch, getState }) => {
    try {
      await dispatch(fetchThing({ id }))

      const thing = thingSelector(getState())

      dispatch({ type: 'PROCESS_THING', thing })
    } catch (err) {
      dispatch(showError(`Invalid thing id="${id}".`))
    }
  }
}

Maybe @winstonewert wants that if the 2nd action dispatch failes, then we rollback the 2 actions.

No. I wouldn't write an action creator that dispatches two actions. I'd define a single action that did two things. The OP seems to prefer action creators that dispatch smaller actions which allows the transient invalid states I dislike.

At the point when your reducer updates the users collection, your store will be temporarily invalid- because redux-search will reference an id that no longer exists in the collection. Once the middleware has run again it will correct the invalid state. This sort of thing can happen anytime one node of your tree is related to another node.

This is actually the kind of case that bothers me. To my mind, the index would ideally be something handled entirely by the reducer or a selector. Having to dispatch extra actions to keep the search up-to-date seems a less pure use of redux.

The OP seems to prefer action creators that dispatch smaller actions which allows the transient invalid states I dislike.

Not exactly. I'd favor single actions when in regard to your action-creator's node of the state-tree. But if a single, conceptual user "action" affects multiple nodes of the state-tree then you'll need to dispatch multiple actions. You can separately invoke each action (which I think is _bad_) or you could have a single action-creator dispatch the actions (the redux-thunk way, which I think is _better_ because it hides that information from your view layer).

This is actually the kind of case that bothers me. To my mind, the index would ideally be something handled entirely by the reducer or a selector. Having to dispatch extra actions to keep the search up-to-date seems a less pure use of redux.

You're not dispatching extra actions. Search is a middleware. It's automatic. But there does exist a transient state when the two nodes of your tree do not agree.

@bvaughn Oh, sorry for being such a purist!

Well, impure code has to do with data fetching and other side-effects/IO, whereas pure code can not trigger any side effect. See this table for comparison between pure and impure code.

Flux best practices says that an action should ā€œdescribe a userā€™s action, are not setters.ā€ Flux docs also hinted further where these actions are supposed to come from:

When new data enters the system, whether through a person interacting with the application or through a web api call, that data is packaged into an action ā€” an object literal containing the new fields of data and a specific action type.

Basically, actions are facts/data that describes ā€œwhat happened,ā€ not what should happen. Stores can only react to these actions synchronously, predictably, and without any other side effect. All other side effects should be handled in action creators (or sagas :wink:).

Example

Iā€™m not saying this is the best way or better than any other way, or even a good way. But this is what I currently consider as best practice.

For example, letā€™s say the user wants to view the scoreboard which requires connection to a remote server. Hereā€™s what should happen:

  • User clicks on view scoreboard button.
  • The scoreboard view displays with a loading indicator.
  • A request is sent to the server to fetch the scoreboard.
  • Wait for the the response.
  • If itā€™s successful, display the scoreboard.
  • If itā€™s failed, the scoreboard closes and a message box appears with an error message. User can close it.
  • User can close the scoreboard.

Assuming actions can only reach the store as a result of userā€™s action or server response, we can create 5 actions.

  • SCOREBOARD_VIEW (as a result of user clicking the view scoreboard button)
  • SCOREBOARD_FETCH_SUCCESS (as a result of successful response from the server)
  • SCOREBOARD_FETCH_FAILURE (as a result of error response from the server)
  • SCOREBOARD_CLOSE (as a result of user clicking the close button)
  • MESSAGE_BOX_CLOSE (as a result of user clicking the close button on the message box)

These 5 actions are sufficient to handle all the requirements above. You can see that the first 4 actions have nothing to do with any "duck". Every action only describes what happened in the outside world (user wants to do this, server said that) and can be consumed by any reducer. We also donā€™t have MESSAGE_BOX_OPEN action, because thatā€™s not ā€œwhat happenedā€ (although thatā€™s what should happen).

The only way to change the state tree is to emit an action, an object describing what happened. ā€”Reduxā€™s README

They are glued together with these action creators:

function viewScoreboard () {
  return async function (dispatch) {
    dispatch({ type: 'SCOREBOARD_VIEW' })
    try {
      const result = fetchScoreboardFromServer()
      dispatch({ type: 'SCOREBOARD_FETCH_SUCCESS', result })
    } catch (e) {
      dispatch({ type: 'SCOREBOARD_FETCH_FAILURE', error: String(e) })
    }
  }
}
function closeScoreboard () {
  return { type: 'SCOREBOARD_CLOSE' }
}

Then each part of the store (governed by reducers) can then react to these actions:

| Part of Store/Reducer | Behavior |
| --- | --- |
| scoreboardView | Update visibility to true on SCOREBOARD_VIEW, false on SCOREBOARD_CLOSE and SCOREBOARD_FETCH_FAILURE |
| scoreboardLoadingIndicator | Update visibility to true on SCOREBOARD_VIEW, false on SCOREBOARD_FETCH_* |
| scoreboardData | Update data inside store on SCOREBOARD_FETCH_SUCCESS |
| messageBox | Update visibility to true and store message on SCOREBOARD_FETCH_FAILURE, and update visibility to false on MESSAGE_BOX_CLOSE |

As you can see, a single action can affect many parts of the store. Stores are only given high-level description of an action (what happened?) rather than a command (what to do?). As a result:

  1. Itā€™s easier to pinpoint errors.

Nothing can affect the state of the message box. No one can tell it to open for any reason. It only reacts to what itā€™s subscribed to (user actions and server responses).

For example, if the server fails to fetch the scoreboard, and a message box did not appear, you do not need to find out why SHOW_MESSAGE_BOX action is not dispatched. It becomes obvious that the message box did not handle the SCOREBOARD_FETCH_FAILURE action properly.

A fix is trivial and can be hot-reloaded and time-traveled.

  1. Action creators and reducers can be tested separately.

You can test whether action creators described what happens in the outside world correctly, without any regard on how stores react to them.

In the same way, reducers can simply be tested whether it reacts properly to the actions from the outside world.

(An integration test would still be very useful.)

No worries. :) I appreciate the further clarification. It actually sounds like we're agreeing here. Looking at your example action-creator, viewScoreboard, it looks a lot like my example action-creator fetchAndProcessThing, right above it.

Action creators and reducers can be tested separately.

While I agree with this, I think that it often makes more pragmatic sense to test them together. It's likely that either your action _or_ or your reducer (maybe both) are super simple and so the return-on-effort value of testing the simple one in isolation is kind of low. That's why I proposed testing the action-creator, reducer, and related selectors together (as a "duck").

But if a single, conceptual user "action" affects multiple nodes of the state-tree then you'll need to dispatch multiple actions.

That's precisely where I think what you are doing differs from what is considered best practices for redux. I think the standard way is to have one action which multiple nodes of the state tree respond to.

Ah, interesting observation @winstonewert. We've been following a pattern of using unique type-constants for each "ducks" bundle and so by extension, a reducer only response to actions dispatched by its sibling action-creators. I'm honestly not sure how I feel, initially, about arbitrary reducers responding to an action. It feels like a little like bad encapsulation.

We've been following a pattern of using unique type-constants for each "ducks" bundle

Note we don't endorse it anywhere in the docs ;-) Not saying it's bad, but it gives people certain sometimes-wrong ideas about Redux.

so by extension, a reducer only response to actions dispatched by its sibling action-creators

There's no such thing as reducer / action creator pairing in Redux. That's purely a Ducks thing. Some people like it but it obscures the fundamental strengths of Redux/Flux model: state mutations are decoupled from each other and from the code causing them.

I'm honestly not sure how I feel, initially, about arbitrary reducers responding to an action. It feels like a little like bad encapsulation.

Depending on what you consider encapsulation boundaries. Actions are global in the app, and I think that's fine. One part of the app might want to react to another part's actions because of complex product requirements, and we think this is fine. The coupling is minimal: all you depend on is a string and the action object shape. The benefit is it's easy to introduce new derivations of the actions in different parts of the app without creating tons of wiring with action creators. Your components stay ignorant of what exactly happens when an action is dispatchedā€”this is decided on the reducer end.

So our official recommendation is that you should first try to have different reducers respond to the same actions. If it gets awkward, then sure, make separate action creators. But don't start with this approach.

We do recommend using selectorsā€”in fact, we recommend exporting keeping functions that read from the state ("selectors") alongside reducers, and always using them in mapStateToProps instead of hardcoding state structure in the component. This way it's easy to change the internal state shape. You can (but don't have to) use reselect for performance, but you can also implement selectors naĆÆvely like in the shopping-cart example.

Perhaps, it boils down to whether you program in imperative style or reactive style. Using ducks can cause actions and reducers to become highly-coupled, which encourages more imperative actions.

  • In imperative style, the store is given ā€œwhat to do,ā€ e.g. SHOW_MESSAGE_BOX or SHOW_ERROR
  • In reactive style, the store is given ā€œa fact of what happened,ā€ e.g. DATA_FETCHING_FAILED or USER_ENTERED_INVALID_THING_ID. The store reacts accordingly.

In previous example, I donā€™t have SHOW_MESSAGE_BOX action or showError('Invalid thing id="'+id+'"') action creator, because thatā€™s not fact. Thatā€™s a command.

Once that fact enters the store, you can translate that fact into commands, inside your pure reducers, e.g.

// type Command = State ā†’ State
// :: Action ā†’ Command
function interpretAction (action) {
  switch (action.type) {
  case 'DATA_FETCHING_FAILED':
    return showErrorMessage('Data fetching failed')
    break
  case 'USER_ENTERED_INVALID_THING_ID':
    return showErrorMessage('User entered invalid thing ID')
    break
  case 'CLOSE_ERROR_MESSAGE':
    return hideErrorMessage()
    break
  default:
    return doNothing()
  }
}

// :: (State, Action) ā†’ State
function errorMessageReducer (state, action) {
  return interpretAction(action)(state)
}

const showErrorMessage = message => state => ({ visible: true, message })
const hideErrorMessage = () => state => ({ visible: false })
const doNothing = () => state => state

When an action goes into the store as ā€œa factā€ rather than ā€œa command,ā€ thereā€™s less chance it can go wrong, because, well, itā€™s a fact.

Now, if your reducers misinterpreted that fact, it can be fixed easily and the fix can travel through time. If your action creators misinterpret that fact, however, you need to re-run your action creators.

You can also change your reducer so that when USER_ENTERED_INVALID_THING_ID fires, the thing ID text field is reset. And that change also travels through time. You can also localize your error message and without refreshing the page. That tightens the feedback loop, and makes debugging and tweaking a lot easier.

(I am just talking about the pros here, of course there are cons. You have to think a lot more about how to represent that fact, given that your store can only respond to these facts synchronously and without side-effects. See discussion about alternatives async/side effect models and this question I posted on StackOverflow. I guess we havenā€™t nailed that part yet.)


I'm honestly not sure how I feel, initially, about arbitrary reducers responding to an action. It feels like a little like bad encapsulation.

Itā€™s also very common for multiple components to take data from the same store. Itā€™s also quite common for a single component to depend on data from multiple parts of the store. Doesnā€™t that too sound a little like bad encapsulation? To become truly modular, shouldnā€™t a React component also be inside the "duck" bundle? (Elm architecture does that.)

React makes your UI reactive (hence its name) by treating data from your store as a fact. So you donā€™t have to tell your view ā€˜how to update the UI.ā€™

In the same way, I also believe Redux/Flux makes your data model reactive, by treating actions as a fact, so you donā€™t have to tell your data model how to update themselves.

Thanks for taking the time to write up and share your thoughts, @dtinth. Also thanks @gaearon for weighing in on this discussion. (I know you have a ton of stuff going on.) You've both given me some additional things to consider. :)

Itā€™s also very common for multiple components to take data from the same store. Itā€™s also quite common for a single component to depend on data from multiple parts of the store. Doesnā€™t that too sound a little like bad encapsulation?

Eh... some of this is subjective, but no. I think of the exported action-creators and selectors as the API for the module.

Anyway, I think this has been a good discussion. Like Thai mentioned in his previous response, there are pros and cons to these approaches we're discussing. It's been nice to get insight into others approaches. :)

By the way message box is a good example of where I'd prefer to have a separate action creator for showing. Mostly because I'll want to pass a time when it was created so it can be dismissed automatically (and action creator is where you call impure Date.now()), because I want to set up a timer to dismiss it, I want to debounce that timer, etc. So I'd consider a message box the case where its "action flow" is important enough to warrant its personal actions. That said perhaps what I described can be solved more elegantly by https://github.com/yelouafi/redux-saga.

I wrote this in the Discord Reactiflux chat initially, but was asked to paste it here.

I've been thinking about the same stuff a lot recently. I feel like state updates are divided to three parts.

  1. The action creator is passed the minimum amount of information needed to execute the update. I.e. anything that can be computed from the current state should not be in it.
  2. The state is queried for any information you need to fulfill the updates (e.g. when you want to copy Todo with id X, you fetch the attributes of Todo with id X so you can make a copy). This can be done in the action creator, and that info is then included in the action object. This results in fat action objects. OR it could be calculated in the reducer - thin action objects.
  3. Based on that information, pure reducer logic is applied to get the next state.

Now, the problem is what to put in the action creator and what in the reducer, the choice between fat and thin action objects. If you put all the logic in the action creator, you end up with fat action objects that basically declare the updates to the state. Reducers become pure, dumb, add-this, remove that, update these functions. They will be easy to compose. But not much of your business logic will be there.

If you put more logic in the reducer, you end up with nice, thin action objects, most of your data logic in one place, but your reducers are harder to compose since you might need info from other branches. You end up with large reducers or reducers that take additional arguments from higher up in the state.

Don't know what the answer to these problems is, not sure if there is one yet

Thanks for sharing these thoughts @tommikaikkonen. I'm still undecided myself about what the "answer" is here. I agree with your summary. I'd add one small note to the "put all the logic in the action creator..." section, which is that it enables you to use (shared) selectors for reading data which in some cases can be nice.

This is an interesting thread! Figuring out where to place the code in a redux app is a problem I guess we all face. I do like the CQRS idea of just recording things that has happened.
But I can see a mismatch of ideas here becuase in CQRS, AFAIK the best practice is to build a de-normlized state from the action/events that is directly consumable by the views. But in redux the best practice is to build a fully normalized state and derieve your views' data through selectors.
If we build denormalized state that is directly consumable by the view, then I think the problem that a reducer wants data in another reducer goes away (becuase each reducer can just store all data it needs not caring about normalization). But then we get other problems when updating data. Maybe this is the core of the discussion?

Coming from many many years of Object Oriented development.. Redux feels like a major step backwards. I find my self begging to create classes that encapsulate events (action creators), and the business logic. I'm still trying to figure out a meaningful compromise but as of yet have been unable to do so. Does anyone else feel the same way?

Object oriented programming encourages putting reads together with writes. This makes a bunch of things problematic: snapshotting and rollback, centralized logging, debugging wrong state mutations, granular efficient updates. If you donā€™t feel that those are the problems to you, if you know how to avoid them while writing traditional object oriented MVC code, and if Redux introduces more problems than it solves in your app, donā€™t use Redux :wink: .

@jayesbe Coming from an object-oriented programming background, you may find that it clashes with emerging ideas in programming. This is one of many articles on the subject: https://www.leaseweb.com/labs/2015/08/object-oriented-programming-is-exceptionally-bad/.

By separating actions from data transformation, testing of application of business rules is simpler. Transforms become less dependent on application context.

That doesn't mean abandoning objects or classes in Javascript. For example, React components are implemented as objects, and now classes. But React components are designed simply to create a projection of supplied data. You are encouraged to use pure components that do not store state.

That is where Redux comes in: to organise application state and bring together actions and the corresponding transformation of application state.

@johnsoftek thanks for the link. However based on my experience in the last 10 years.. I don't agree with it but we don't need to get into the debate between OO and non-OO here. The issue I have is with organizing code and abstraction.

My goal is to create a single app / single architecture that can (using configuration values alone) be used to create 100's of apps. The use case I have to deal with is handling a white-label software solution that is in use by many clients.. each calling the application their own.

I have come up with what I feel is an interesting compromise.. and I think it handles it nicely enough but it may not meet the functional programming crowds standards. I'd still like to put it out there.

I have a single Application class that is self-contained with all the business logic, API wrappers, etc.. that I need to interface with my server side application.

example..

export default Application {
    constructor(config) {
        this.config = config;
    } 

    config() {
        return this.config;
    }

    login(data, cb) {
        const url = [
            this.config.url,
            '?client=' + this.config.client,
            '&username=' + data.username,
            ....
        ].join('');

        fetch(url).then((responseText) => {
            cb(responseText);
        })
    }

    ... more business logic 
}

I created a single instance of this object and placed it into the context.. by extending the Redux Provider

import { Provider } from 'react-redux';

export default class MyProvider extends Provider {
    getChildContext() {
        return Object.assign({}, Provider.prototype.getChildContext.call(this), {
            app: this.props.app
        });
    }

    render() {
        return this.props.children;
    }
}
MyProvider.childContextTypes = {
    store: React.PropTypes.object,
    app: React.PropTypes.object
}

Then I used this provider as such

import Application from './application';
import config from './config';

class MyApp extends Component {
  render() {
    return (
      <MyProvider store={store} app={new Application(config)}>
        <Router />
      </MyProvider>
    );
  }
}

AppRegistry.registerComponent('MyApp', () => MyApp);

finally in my Component I used my app..

class Login extends React.Component {
    render() {
        const { app } = this.context;
        const { state, actions } = this.props;
        return (
              <View style={style.transparentContainer}>
                <Form ref="form" type={User} options={options} />
                <Button 
                  onPress={() => {
                    value = this.refs.form.getValue();
                    if (value) {
                      app.login(value, actions.login);
                    }
                  }}
                >
                  Login
                </Button>
              </View>
        );
    }
};
Login.contextTypes = {
  app: React.PropTypes.object,
};

function mapStateToProps(state) {
  return {
      state: state.default.auth
  };
};

function mapDispatchToProps(dispatch) {
  return {
    actions: bindActionCreators(authActions, dispatch),
    dispatch
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(Login);

The Action Creator is thus just a callback function to my business logic.

                      app.login(value, actions.login);

This solution seems to be working nicely at the moment though I've only started with authentication.

I believe I could also pass the store into my Application instance but I don't want to do that because I don't want the store to undergo chance mutation. Though accessing the store may come in handy. I'll think about that more if I need to.

I have come up with what I feel is an interesting compromise.. and I think it handles it nicely enough but it may not meet the functional programming crowds standards.

You wonā€™t find ā€œthe functional crowdā€ here :wink: . The reason we choose functional solutions in Redux is not because weā€™re dogmatic but because they solve some problems people often make because of classes. For example, separating reducers from action creators lets us separate reads and writes which is important for logging and reproducing bugs. Actions being plain objects make record and replay possible because they are serializable. Similarly, state being plain object rather than an instance of MyAppState makes it very easy to serialize it on the server and deserialize it on the client for server rendering, or persist parts of it in localStorage. Expressing reducers as functions allows us to implement time travel and hot reloading, and expressing selectors as functions makes memoization easy to add. All of these benefits have nothing to do with us being a ā€œfunctional crowdā€ and everything to do with solving specific tasks this library was created to solve.

I created a single instance of this object and placed it into the context.. by extending the Redux Provider

This looks totally sensible to me. We donā€™t have an irrational hate of classes. The point is that weā€™d rather not use them in cases where they are severly limiting (such as for reducers or action objects), but itā€™s fine to use them to generate action objects, for example.

I would however avoid extending Provider as this is fragile. There is no need for it: React merges context of components, so you can just wrap it instead.

import { Component } from 'react';
import { Provider } from 'react-redux';

export default class MyProvider extends Component {
    getChildContext() {
        return {
            app: this.props.app
        };
    }

    render() {
        return (
            <Provider store={this.props.store}>
                {this.props.children}
            </Provider>
        );
    }
}
MyProvider.childContextTypes = {
    app: React.PropTypes.object
}
MyProvider.propTypes = {
    app: React.PropTypes.object,
    store: React.PropTypes.object
}

It actually reads easier in my view, and is less fragile.

So, all in all, your approach makes total sense. Using a class in this case is not really different from something like createActions(config) which is a pattern we also recommend if you need to parametrize the action creators. Thereā€™s absolutely nothing wrong with it.

We only discourage you from using class instances for state and action objects because class instances make serialization very tricky. For reducers, we also donā€™t recommend using classes because it will be harder to use reducer composition, that is, reducers that call other reducers. For everything else, you can use any means of code organization, including classes.

If your application and configuration are immutable (and I think they should be, but perhaps I've drunk too much functional cool-aid), then you could consider the following approach:

const appSelector = createSelector(
   (state) => state.config,
   (config) => new Application(config)
)

And then in mapStateToProps:

function mapStateToProps(state) {
  return {
      app: appSelector(state)
  };
};

Then you don't need the provider technique you've adopted, you just obtain the application object from the state. Thanks to reselect, the Application object will only be constructed when the config changes, which is probably just once.

Where I think this approach may have an advantage is that it easily lets you extend the idea to having multiple such objects and also having those objects depend on other parts of the state. For example, you could have a UserControl class with login/logout/etc methods that has access both to the configuration and part of your state.

So, all in all, your approach makes total sense.

:+1: Thanks that helps. I agree with the improvement on MyProvider. I'll update my code to follow. One of the biggest problems I had when first learning Redux was the semantic notion of "Action Creators" .. it didn't jive until I equated them with Events. For me it was kind of realization that was like.. these are events that are being dispatched.

@winstonewert is createSelector available on react-native ? I don't believe it is. At the same time, it does look as though you're creating the new Application every time you attach it in mapStateToProps at some component ? My objective is to have a single object instantiated that provides for all business logic to the application and for that object to be accessible globally. I'm not sure if you're suggestion works. Though I like the idea of having additional objects available if needed.. technically I can instantiate as necessary through the Application instance as well.

One of the biggest problems I had when first learning Redux was the semantic notion of "Action Creators" .. it didn't jive until I equated them with Events. For me it was kind of realization that was like.. these are events that are being dispatched.

I would say that there is no semantic notion of Action Creators in Redux at all. There is a semantic notion of Actions (which describe what happened and are roughly equivalent to events but not quiteā€”e.g. see discussion in #351). Action creators are just a pattern for organizing the code. Itā€™s convenient to have factories for actions because you want to make sure that actions of the same type have consistent structure, have the same side effect before they are dispatched, etc. But from Redux point of view, action creators donā€™t existā€”Redux only sees actions.

is createSelector available on react-native ?

It is available in Reselect which is plain JavaScript without dependencies and can work on the web, on the native, on the server, etc.

Ahh. ok got it. Things are a lot more clear. Cheers.

Do not nest objects in mapStateToProps and mapDispatchToProps

I recently ran into an issue where nested objects were lost when Redux merges mapStateToProps and mapDispatchToProps (see reactjs/react-redux#324). Although @gaearon provides a solution that allows me to use nested objects, he goes on to say that this is an anti-pattern:

Note that grouping objects like this will cause unnecessary allocations and will also make performance optimizations harder because we can no longer rely on shallow equality of result props as a way to tell whether they changed. So you will see more renders than with a simple approach without namespacing which we recommend in the docs.

@bvaughn said

reducers should be stupid and simple

and we should put most business logic into action creators, I'm absolutely agree with that. But if everything has moved into actions, why we still have to create reducer files and functions manually? Why not put the data which has been operated in actions into store directly?

It has confused me a period of time...

why we still have to create reducer files and functions manually?

Because reducers are pure functions, if there is an error in state-updating logic, you can hot-reload the reducer. The dev tool can then rewind the app to its initial state, and then replay all the actions, using the new reducer. This means you can fix state-updating bugs without having to manually roll-back and re-perform the action. This is the benefit of keeping the majority of state-updating logic in the reducer.

This is the benefit of keeping the majority of state-updating logic in the reducer.

@dtinth Just to clarify, by saying "state-updating logic" do you mean "business logic"?

I'm not so sure putting most of your logic in action creators is a good idea. If all your reducers are just trivial functions accepting ADD_X-like actions, then they're unlikely to have errors - great! But then all your errors have been pushed to your action creators and you lose the great debugging experience that @dtinth alludes to.

But also like @tommikaikkonen mentioned, it's not so simple writing complex reducers. My gut feeling is that is where you would want to push if you wanted to reap the benefits of Redux though - otherwise instead of pushing side effects to the edge, you're pushing your pure functions to handle only the most trivial tasks, leaving most of your app in state-ridden hell. :)

@sompylasar "business logic" and "state-updating logic" are, imo, kind of the same thing.

However to chime in with my own implementation specifics.. my Actions are primarily lookups on inputs into the Action. Actually all my Actions are pure as I have moved all my "business logic" into an application context.

as an example.. this my typical reducer

export default function reducer(state = initialState, action = {}) {
  switch (action.type) {
    case 'FOO_REQUEST':
    case 'FOO_RESPONSE':
    case 'FOO_ERROR':
    case 'FOO_RESET':
      return {
        ...state,
        ...action.data
      }; 
    default:
      return state;
  }
}

My typical actions:

export function fooRequest( res ) {
  return {
    type: 'FOO_REQUEST',
    data: {
        isFooing: true,
        toFoo: res.saidToFoo
    }
  };
}

export function fooResponse( res ) {
  return {
    type: 'FOO_RESPONSE',
    data: {
        isFooing: false,
        isFooed: true,
        fooData: res.data
    }
  };
}

export function fooError( res ) {
  return {
    type: 'FOO_ERROR',
    data: {
        isFooing: false,
        fooData: null,
        isFooed: false,
        fooError: res.error
    }
  };
}

export function fooReset( res ) {
  return {
    type: 'FOO_RESET',
    data: {
        isFooing: false,
        fooData: null,
        isFooed: false,
        fooError: null,
        toFoo: true
    }
  };
}

My business logic is defined in an object stored in the context, ie..

export default class FooBar
{
    constructor(store)
    {
        this.actions = bindActionCreators({
            ...fooActions
        }, store.dispatch);
    }

    async getFooData()
    {
        this.actions.fooRequest({
            saidToFoo: true
        });

        fetch(url)
        .then((response) => {
            this.actions.fooResponse(response);
        })
    }
}

If you see my comment above I was also struggling with the best approach.. I finally refactored and settled with passing the store into the constructor of my application object and connecting all the actions to the dispatcher at this central point. All the actions my application knows about are assigned here.

I no longer use the mapDispatchToProps() anywhere. For Redux I now only mapStateToProps when creating a connected component. If I need to trigger any actions.. I can trigger them through my application object via the context.

class SomeComponent extends React.Component {
    componentWillReceiveProps(nextProps) {
        if (nextProps.someFoo != this.props.someFoo) {
            const { app } = this.context;
            app.actions.getFooData();
        }
    }
}
SomeComponent.contextTypes = {
    app: React.PropTypes.object
};

The above component doesnt need to be redux connected. It can still dispatch actions. Of course if you need to update state within the component you would turn it into a connected component to make sure that state change is propagated.

This is how I organized my core "business logic". Since my state is maintained really on a backend serrver.. this works really well for my use case.

Where you store your "business logic" is really up to you and how it fits your use case.

@jayesbe The following part means you have no "business logic" in the reducers, and, moreover, the state structure has moved into the action creators which create the payload that is transferred into the store via the reducer:

    case 'FOO_REQUEST':
    case 'FOO_RESPONSE':
    case 'FOO_ERROR':
    case 'FOO_RESET':
      return {
        ...state,
        ...action.data
      }; 
export function fooRequest( res ) {
  return {
    type: 'FOO_REQUEST',
    data: {
        isFooing: true,
        toFoo: res.saidToFoo
    }
  };
}

@jayesbe My actions and reducers are very similar to yours, some actions receive a plain network response object as argument, and I encapsulated the logic how to handle the response data into the action and finally return a very simple object as returned value then pass to reducer via call dispatch(). Just like what you did. The problem is if your action written in this way, your action have done almost everything and the responsibility of your reducer will be very lightweight, why we have to transfer the data to store manually if the reducer just spreads the action object simply? Redux dose it automatically for us is not a tough thing at all.

Not necessarily. But a lot of the time, part of the business process
involves updating the application's state according to the business rules,
so you might have to put some business logic in there.

For an extreme case, check this out:
ā€œSynchronous RTS Engines and a Tale of Desyncsā€ @ForrestTheWoods
https://blog.forrestthewoods.com/synchronous-rts-engines-and-a-tale-of-desyncs-9d8c3e48b2be

On Apr 5, 2016 5:54 PM, "John Babak" [email protected] wrote:

This is the benefit of keeping the majority of state-updating logic in the
reducer.

@dtinth https://github.com/dtinth Just to clarify, by saying
"state-updating logic" do you mean "business logic"?

ā€”
You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub
https://github.com/reactjs/redux/issues/1171#issuecomment-205754910

@LumiaSaki The advice to keep your reducers simple while keeping complex logic in action creators goes against the recommended way to use Redux. Redux's recommended pattern is the opposite: keep action creators simple while keeping complex logic in reducers. Of course, you are free to put all your logic in action creators anyways, but in doing so you aren't followed the Redux paradigm, even if you are using Redux.

Because of that, Redux won't automatically transfer data from actions to the store. Because that's not the way you are supposed to be using Redux. Redux isn't going to be changed to facilitate using it in a way other then it was intended. Of course, you are absolutely free to do what works for you, but don't expect Redux to change for it.

For what its worth, I produce my reducers using something like:

let {reducer, actions} = defineActions({
   fooRequest: (state, res) => ({...state, isFooing: true, toFoo: res.saidToFoo}),
   fooResponse: (state, res) => ({...state, isFooing: false, isFooed: true, fooData: res.data}),
   fooError: (state, res) => ({...state, isFooing: false, fooData: null, isFooed: false, fooError: res.error})
   fooReset: (state, res) => ({...state, isFooing: false, fooData: null, isFooed: false, fooError: null, toFoo: false})
})

defineActions returns both the reducer and the action creators. This way I find it very easy to keep my update logic inside the reducer while not having to spend a lot of time writing trivial action creators.

If you insist on keeping your logic in your action creators, then you shouldn't have any trouble automating the data yourself. Your reducer is a function, and it can do whatever it wants. So your reducer could be as simple as:

function reducer(state, action) {
    if (action.data) {
        return {...state, ...action.data}
   } else {
        return state;
   }
}

Expanding on the previous points regarding business logic, I think you can separate your business logic into two parts:

  • The non-deterministic part. This part makes use of external services, asynchronous code, system time, or a random number generator. In my opinion this part is best handled by an I/O function (or an action creator). I call them the business process.

Consider an IDE-like software where user can click the run button, and it would compile and run the app. (Here I use an async function that takes a store, but you can use redux-thunk instead.)

js export async function runApp (store) { try { store.dispatch({ type: 'startCompiling' }) const compiledApp = await compile(store) store.dispatch({ type: 'startRunning', app: compiledApp }) } catch (e) { store.dispatch({ type: 'errorCompiling', error: e }) } }

  • The deterministic part. This part has totally predictable result. Given the same state and the same event, the result is always predictable. In my opinion this part is best handled by a reducer. I call this the business rules.

``` js
import u from 'updeep'

export const reducer = createReducer({
// [action name]: action => currentState => nextState
startCompiling: () => u({ compiling: true }),
errorCompiling: ({ error }) => u({ compiling: false, compileError: error }),
startRunning: ({ app }) => u({
running: () => app,
compiling: false
}),
stopRunning: () => u({ running: false }),
discardCompileError: () => u({ compileError: null }),
// ...
})
```

I try to put as much code into this deterministic land as possible, while keeping in mind that the reducerā€™s sole responsibility is to keep the application state consistent, given the incoming actions. Nothing more. Anything besides that, I would do it outside of Redux, because Redux is just a state container.

@dtinth Great, because the previous example in https://github.com/reactjs/redux/issues/1171#issuecomment-205782740 looks totally different to what you've written in https://github.com/reactjs/redux/issues/1171#issuecomment-205888533 -- it suggests to construct a piece of state in action creators and pass them into the reducers for them to just spread the updates (this approach looks wrong to me, and I agree with the same pointed out in https://github.com/reactjs/redux/issues/1171#issuecomment-205865840 ).

@winstonewert

Redux's recommended pattern is the opposite: keep action creators simple while keeping complex logic in reducers.

How can you put complex logic in the ruducers and still keep them pure ?

If I am calling fetch() for example and loading data from the server.. then processing it in some way. I have yet to see an example of a reducer that has "complex logic"

@jayesbe : Uh... "complex" and "pure" are orthogonal. You can have _really_ complicated conditional logic or manipulation inside a reducer, and as long as it's just a function of its inputs with no side effects, it's still pure.

If you have complex local state (think a post editor, tree view, etc) or handle things like optimistic updates, your reducers will contain complex logic. It really depends on the app. Some have complex requests, others have complex state updates. Some have both :-)

@markerikson ok logic statements are one thing.. but executing specific tasks? Like say I have one action that in one case triggers three other actions, or in another case triggers two distinct and separate actions. That logic + execution of tasks doesn't sound like they should go in reducers.

My state data / model state is on the server, view state is distinct of data model but the management of that state is on the client. My data model state is simply passed down into the view.. which is what makes my reducers and actions so lean.

@jayesbe : I don't think anyone ever said that triggering of other actions should go in a reducer. And in fact, it shouldn't. A reducer's job is simply (currentState + action) -> newState.

If you need to tie together multiple actions, you either do it in something like a thunk or saga and fire them off in sequence, or have something listening to state changes, or use a middleware to intercept an action and do additional work.

I'm kinda confused on what the discussion is at this point, to be honest.

@markerikson the topic seems to be about the "business logic" and where it goes. The fact of the matter is.. it all depends on the application. There are different ways to go about it. Some more complex then others. If you find a good way to solve your problem that makes things easy for you to maintain and organize. That's all that really matters. My implementation happens to be very lean for my use case even if it goes against the paradigm.

As you note, all that really matters is what works for you. But here's how I'd tackle the issues you asked about.

If I am calling fetch() for example and loading data from the server.. then processing it in some way. I have yet to see an example of a reducer that has "complex logic"

My reducer takes a raw response from my server and updates my state with it. That way the processing response you talk about is done in my reducer. For example, the request might be fetch JSON records for my server, which the reducer sticks in my local records cache.

k logic statements are one thing.. but executing specific tasks? Like say I have one action that in one case triggers three other actions, or in another case triggers two distinct and separate actions. That logic + execution of tasks doesn't sound like they should go in reducers.

That depends on what you are doing. Obviously, in the server fetch case one action will trigger another. That's fully within recommended Redux procedure. However, you might also be doing something like this:

function createFoobar(dispatch, state, updateRegistry) {
   dispatch(createFoobarRecord());
   if (updateRegistry) {
      dispatch(updateFoobarRegistry());
   } else {
       dispatch(makeFoobarUnregistered());
   }
   if (hasFoobarTemps(state)) {
      dispatch(dismissFoobarTemps());
   }
}

This isn't the recommended way to use Redux. The recommended Redux way is to have a single CREATE_FOOBAR action which causes all of these desired changes.

@winstonewert :

This isn't the recommended way to use Redux. The recommended Redux way is to have a single CREATE_FOOBAR action which causes all of these desired changes.

You have a pointer to somewhere that's specified? Because when I was doing research for the FAQ page, what I came up with was "it depends", direct from Dan. See http://redux.js.org/docs/FAQ.html#actions-multiple-actions and this answer by Dan on SO.

"Business logic" is really a pretty broad term. It can cover stuff like "Has a thing happened?", "What do we do now that this happened?", "Is this valid?", and so on. Based on Redux's design, those questions _can_ be answered in various places depending on what the situation is, although I would see "has it happened" as more of an action creator responsibility, and "what now" is almost definitely a reducer responsibility.

Overall, my take on this _entire question_ of "business logic" is: _"it depends_". There's reasons why you might want to do request parsing in an action creator, and reasons why you might want to do it in a reducer. There's times when your reducer might simply be "take this object and slap it into my state", and other times when your reducer might be very complex conditional logic. There's times when your action creator might be very simple, and other times when it could be complex. There's times when it makes sense to dispatch multiple actions in a row to represent steps of a process, and other times when you'd only want to dispatch a generic "THING_HAPPENED" action to represent all of it.

About the only hard-and-fast rule I'd agree with is "non-determinism in action creators, pure determinism in reducers". That's a given.

Other than that? Find something that works for you. Be consistent. Know why you're doing it a certain way. Go with it.

although I would see "has it happened" as more of an action creator responsibility, and "what now" is almost definitely a reducer responsibility.

That's why there is a parallel discussion how to put side-effects, i.e. the non-pure part of the "what now", into reducers: #1528 and make them just pure descriptions of what should happen, like the next actions to dispatch.

The pattern I've been using is:

  • What To Do: Action/Action Creator
  • How To Do It: Middleware (for example, middleware that listens for 'async' actions and makes calls to my API object.)
  • What To Do With Result: Reducer

From earlier in this thread, Dan's statement was:

So our official recommendation is that you should first try to have different reducers respond to the same actions. If it gets awkward, then sure, make separate action creators. But don't start with this approach.

From that, I take it that the recommended approach is to dispatch one action per event. But, pragmatically, do what works.

@winstonewert : Dan's referring to the "reducer composition" pattern, ie, "is an action only ever listened to by one reducer" vs "many reducers can respond to the same action". Dan is very big on arbitrary reducers responding to a single action. Others prefer stuff like the "ducks" approach, where reducers and actions are VERY tightly bundled, and only one reducer ever handles a given action. So, that example's not about "dispatching multiple actions in sequence", but rather "how many portions of my reducer structure are expecting to respond to this".

But, pragmatically, do what works.

:+1:

@sompylasar I see the error of my ways by having the state structure in my Actions. I can easily shift the state structure into my reducers and simplify my actions. Cheers.

It seems to me that its the same thing.

Either you have a single action triggering multiple reducers causing multiple state changes, or you have a multiple actions each triggering a single reducer causing a single state change. Having multiple reducers respond to an action and having an event dispatch multiple actions are alternative solutions to the same problem.

In the StackOverflow question you mention, he states:

Keep action log as close to the history of user interactions as you can. However if it makes reducers tricky to implement consider splitting some actions in several, if a UI update can be thought of two separate operations that just happen to be together.

As I see it, Dan endorses maintaining one action per user interaction as the ideal way. But he's pragmatic, when it makes the reducer's tricky to implement he endorses splitting the action.

I'm visualizing a couple similar but somewhat different use cases here:

1) An action requires updates to multiple areas of your state, particularly if you're using combineReducers to have separate reducer functions handling each sub-domain. Do you:

  • have both Reducer A and Reducer B respond to the same action and update their bits of state independently
  • have your thunk stuff ALL relevant data into the action so any reducer can access other bits of state outside its own chunk
  • add another top-level reducer that grabs bits of Reducer A's state and special-case hand it to Reducer B?
  • dispatch an action intended for Reducer A with the bits of state it needs, and a second action for Reducer B with what it needs?

2) You've got a set of steps that happen in a specific sequence, each step requiring some state update or middleware action. Do you:

  • Dispatch each individual step as a separate action to represent the progress, and let individual reducers respond to specific actions?
  • Dispatch one big event, and trust that the reducers handle it appropriately?

So yeah, definitely some overlap, but I think part of the difference in mental picture here is the various use cases.

@markerikson So your advice is 'it depends on what situation you met', and how to balance 'business logic' on actions or reducers is just up to your consideration, we should also take benefits of pure function as much as possible?

Yeah. Reducers _have_ to be pure, as a Redux requirement (except in 0.00001% of special cases). Action creators absolutely _do not_ have to be pure, and in fact are where most of your "impurities" will live. However, since pure functions are obviously easier to understand and test than impure functions, _if_ you can make some of your action creation logic pure, great! If not, that's fine.

And yes, from my point of view, it's up to you as a developer to determine what an appropriate balance is for your own app's logic and where it lives. There is no single hard-and-fast rule for which side of the action creator / reducer divide it should live on. (Erm, except for the "determinism / non-determinism" thing I mentioned above. Which I clearly meant to reference in this comment. Obviously.)

@cpsubrian

What To Do With Result: Reducer

Actually this is why sagas are for: to deal with effects like "if this happened, then that should also happen"


@markerikson @LumiaSaki

Action creators absolutely do not have to be pure, and in fact are where most of your "impurities" will live.

Actually action creators are not even required to be impure or to even exist.
See http://stackoverflow.com/a/34623840/82609

And yes, from my point of view, it's up to you as a developer to determine what an appropriate balance is for your own app's logic and where it lives. There is no single hard-and-fast rule for which side of the action creator / reducer divide it should live on.

Yes but it's not so obvious to notice the drawbacks of each approach without experience :) See also my comment here: https://github.com/reactjs/redux/issues/1171#issuecomment-167585575

No strict rule works fine for most simple apps, but if you want to build reusable components, these components should not be aware of something outside their own scope.

So instead of defining a global action list for your whole app, you can start splitting your app into reusable components and each component has its own list of actions, and can only dispatch/reduce these. The problem is then, how do you express "when date selected in my date picker, then we should save a reminder on that todo item, show a feedback toast, and then navigate the app to the todos with reminders": this is where the saga comes to action: orchestrating the components

See also https://github.com/slorber/scalable-frontend-with-elm-or-redux

And yes, from my point of view, it's up to you as a developer to determine what an appropriate balance is for your own app's logic and where it lives. There is no single hard-and-fast rule for which side of the action creator / reducer divide it should live on.

Yes, there is no requirement from Redux whether you put your logic in the reducers or action creators. Redux won't break either way. There is no hard and fast rule that requires you to do it one way or the other. But Dan's recommendation was to "Keep action log as close to the history of user interactions as you can." Dispatching a single action per user event isn't required, but it is recommended.

In my case I have 2 reducers interested in 1 action. The raw action.data is not enough. They need to handle a transformed data. I didn't want to perform the transformation in the 2 reducers. So I moved the function to perform transformation to a thunk. This way my reducers receive a data ready for consumption. This is the best I could think in my short 1month redux experience.

What about decoupling the components/views from the structure of the store? my goal is that anything that is affected by the structure of the store should be manage in the reducers, that is why i like to colocate selectors with reducers, so components don't really need to know how get a particular node of the store.

That's great for passing data to the components, what about the other way around, when components dispatch actions:

Let say for example in a Todo app i'm updating the name of a Todo item, so i dispatch an action passing the portion of the item i want to update i.e.:

dispatch(updateItem({name: <text variable>}));

, and the action definition is:

const updateItem = (updatedData) => {type: "UPDATE_ITEM", updatedData}

which in turn is handle by the reducer which could simply do:

Object.assign({}, item, action.updatedData)

to update the item.

This works great as i can reuse the same action and reducer to update any prop of the Todo item, ie:

updateItem({description: <text variable>})

when the description is changed instead.

But here the component needs to know how a Todo item is defined in the store and if that definition changes i need to remember to change it in all the components that depend on it, which is obviously a bad idea, any suggestions for this scenario?

@dcoellarb

My solution in this sort of situation is to take advantage of Javascript's flexibility to generate what would be boilerplate.

So I might have:

const {reducer, actions, selector} = makeRecord({
    name: TextField,
    description: TextField,
    completed: BooleanField
})

Where makeRecord is a function to automatically build reducers, action creators, and selectors from my description. That eliminates the boilerplate, but if I need to do something which doesn't fit this neat pattern later, I can add custom reducer/actions/selector to the result of makeRecord.

tks @winstonewert i like the aproach to avoid the boilerplate, i can see that saving a lot of time in apps with a lot of models; but i still don't see how this will decouple the component from store structure, i mean even if the action is generated the component will still need to pass the updated fields to it, which means that the component still needs to know the structure of the store right?

@winstonewert @dcoellarb In my opinion, the action payload structure should belong to actions, not to reducers, and be explicitly translated into state structure in a reducer. It should be a lucky coincidence that these structures mirror each other for initial simplicity. These two structures don't need to always mirror each other becausr they are not the same entity.

@sompylasar right, i do that, i translate the api/rest data to my store structure, but still the only one that should know the store structure is the reducers and the selectors right? reason why i colocate them, my issue is with the components/views i would prefer them to not need to know the store structure in case i decide to change it later, but as explained in my example they need to know the structure so they can send the right data to be updated, i have not find a better way to do it :(.

@dcoellarb You may think of your views as inputs of data of certain type (like string or number, but structured object with fields). This data object does not necessarily mirror the store structure. You put this data object into an action. This is not coupling with store structure. Both store and view have to be coupled with action payload structure.

@sompylasar makes sense, i'll give it a try, thanks a lot!!!

I should probably also add that you can make actions more pure by using redux-saga. However, redux-saga struggles to handle async events, so you can take take this idea a step further by using RxJS (or any FRP library) instead of redux-saga. Here's an example using KefirJS: https://github.com/awesome-editor/awesome-editor/blob/saga-demo/src/stores/app/AppSagaHandlers.js

Hi @frankandrobot,

redux-saga struggles to handle async events

What do you mean by this? Isn't redux-saga made to handle async events and side-effects in an elegant way? Take a look at https://github.com/reactjs/redux/issues/1171#issuecomment-167715393

No @IceOnFire . Last time I read the redux-saga docs, handling complex async workflows is hard. See, for example: http://yelouafi.github.io/redux-saga/docs/advanced/NonBlockingCalls.html
It said (still says?) something to the effect

we'll leave the rest of the details to the reader because it's starting to get complex

Compare that with the FRP way: https://github.com/frankandrobot/rflux/blob/master/doc/06-sideeffects.md#a-more-complex-workflow
That entire workflow is handled completely. (In only a few lines I might add.) On top of that, you still get most of the goodness of redux-saga (everything is a pure function, mostly easy unit tests).

The last time I thought about this, I came to the conclusion that the problem is redux-saga is makes everything look synchronous. It's great for simple workflows but for complex async workflows, it's easier if you handle the async explicitly... which is what FRP excels in.

Hi @frankandrobot,

Thanks for your explanation. I don't see the quote you mentioned, maybe the library evolved (for example, I now see a cancel effect I never saw before).

If the two examples (saga and FRP) are behaving exactly the same then I don't see much difference: one is a sequence of instructions inside try/catch blocks, while the other is a chain of methods on streams. Due to my lack of experience on streams I even find more readable the saga example, and more testable since you can test every single yield one by one. But I'm quite sure this is due to my mindset more than the technologies.

Anyway I would love to know @yelouafi's opinion on this.

@bvaughn can you point to any decent example of testing action, reducer, selector in the same test as you describe here?

The most efficient way to tests actions, reducers, and selectors is to follow the "ducks" approach when writing tests. This means you should write one set of tests that cover a given set of actions, reducers, and selectors rather than 3 sets of tests that focus on each individually. This more accurately simulates what happens in your real application and it provides the most bang for the buck.

Hi @morgs32 šŸ˜„

This issue is a bit old and I haven't used Redux in a while. There is a section on the Redux site about Writing Tests that you may want to check it out.

Basically I was just pointing out that- rather than writing tests for actions and reducers in isolation- it can be more efficient to write them together, like so:

import configureMockStore from 'redux-mock-store'
import { actions, selectors, reducer } from 'your-redux-module';

it('should support adding new todo items', () => {
  const mockStore = configureMockStore()
  const store = mockStore({
    todos: []
  })

  // Start with a known state
  expect(selectors.todos(store.getState())).toEqual([])

  // Dispatch an action to modify the state
  store.dispatch(actions.addTodo('foo'))

  // Verify the action & reducer worked successfully using your selector
  // This has the added benefit of testing the selector also
  expect(selectors.todos(store.getState())).toEqual(['foo'])
})

This was just my own observation after using Redux for a few months on a project. It's not an official recommendation. YMMV. šŸ‘

"Do more in action-creators and less in reducers"

What if application is server&client and server must contain business logic and validators?
So I send action as is, and most work will be done at server-side by reducer...

First, Sorry for my English. But I have some different opinions.

My choice is fat reducer, thin action creators.

My action creators just __dispatch__ actions(async, sync, serial-async, parallel-async, parallel-async in for-loop) based on some __promise middleware__ .

My reducers split into many small state slice to handle the business logic. use combineReduers combine them. reducer is __pure function__, so it's easy to be re-used. Maybe someday I use angularJS, I think I can re-use my reducer in my service for the same business logic. If your reducer has many lines codes, it maybe can split into smaller reducer or abstract some functions.

Yes, there are some cross state case which meanings A depend on B, C.. and B, C are async datas. We must use B,C to fill or initialize A. So that's why I use crossSliceReducer.

About __Do more in action-creators and less in reducers__.

  1. If you use redux-thunk or etc. Yeah. You can access to the complete state within action creators by getState(). This is a choice. Or, You can create some __crossSliceReducer__, so you can access to the complete state too, you can use some state slice to compute your other state.

About __Unit testing__

reducer is __pure function__. So it's easy to be testing. For now, I just test my reducers, because it's most important than other part.

To test action creators ? I think if they are "fat", it maybe not easy to be testing. Especially __async action creators__.

I agree with you @mrdulin that's now the way I've gone too.

@mrdulin Yeah. It looks like middleware is the right place to place your impure logic.
But for business logic reducer does not seem like a right place. You'll end up with mutliple "synthetic" actions that don't represent what user has asked but what your business logic requires.

Much simpler choice is just call some pure functions/class methods from the middleware:

middleware = (...) => {
  // if(action.type == 'HIGH_LEVEL') 
  handlers[action.name]({ dispatch, params: action.payload })
}
const handlers = {
  async highLevelAction({ dispatch, params }) {
    dispatch({ loading: true });
    const data = await api.getData(params.someId);
    const processed = myPureLogic(data);
    dispatch({ loading: false, data: processed });
  }
}

@bvaughn

This reduces the likelihood of mistyped variables that lead to subtle undefined values, it simplifies changes to the structure of your store, etc.

My case against using selectors everywhere, even for trivial pieces of state that don't require memoization or any sort of data transformation:

  • Shouldn't unit tests for reducers already catch mistyped state properties?
  • Changes to the structure of the store don't occur that often in my experience, mostly at the ramp-up phase of a project. Later on, if you change state.stuff.item1 to state.stuff.item2 you search through the code and change it everywhere - just like changing the name of anything else really. It's a common task and a non-brainer for people using IDEs especially.
  • Using selectors everywhere is kind of an empty abstraction. Why is there an API in-between the store and other redux code? The whole benefit of having a all application state in a simple, unified object is that is easy to access. There is also the overhead of importing/exporting the selectors everyhwere, testing them, finding the right one. I never understood this concern of accessing simple pieces of state directly. Sure, you gain consistency by having this API to access the state, but you give up on simplicity.

Obviously selectors are necessary, but I'd like to hear some other arguments for making them a mandatory API.

From a practical point of view, one good reason in my experience is that libraries such as reselect or redux-saga leverage selectors to access pieces of state. This is enough for me to stick with selectors.

Philosophically speaking, I always see selectors as the "getter methods" of the functional world. And for the same reason why I would never access public attributes of a Java object, I would never access substates directly in a Redux app.

@IceOnFire There's nothing to leverage if the computation isn't expensive or data transformation isn't required.

Getter methods might be common practice in Java but so is accessing POJOs directly in JS.

@timotgl

Why is there an API in-between the store and other redux code?

Selectors are a reducer's query (read) public API, actions are a reducer's command (write) public API. Reducer's structure is its implementation detail.

Selectors and actions are used in the UI layer, and in the saga layer (if you use redux-saga), not in the reducer itself.

@sompylasar Not sure I follow your point of view here. There is no alternative to actions, I must use them to interact with redux. I don't have to use selectors however, I can just pick something directly from the state when it is exposed, which it is by design.

You're describing a way to think about selectors as a reducer's "read" API, but my question was what justifies making selectors a mandatory API in the first place (mandatory as in enforcing that as a best practice in a project, not by library design).

@timotgl Yes, selectors are not mandatory. But they make a good practice to prepare for future changes in the app code, making possible to refactor them separately:

  • how certain piece of state is structured and written to (the reducer concern, one place)
  • how that piece of state is used (the UI and side-effects concerns, many places where the same piece of state is queried from)

When you're about to change the store structure, without selectors you'll have to find and refactor all the places where the affected pieces of state are accessed, and this could potentially be a non-trivial task, not simply find-and-replace, especially if you pass around the fragments of state obtained directly from the store, not via a selector.

@sompylasar Thanks for your input.

this could potentially be a non-trivial task

That's a valid concern, it just seems like an expensive tradeoff to me. I guess I haven't come across a state refactoring that caused such problems. I have come across "selector spaghetti" however, where nested selectors for every trivial sub-piece of state caused quite the confusion. This counter-measure itself has to be maintained as well after all. But I understand the reason behind this better now.

@timotgl A simple example that I can share publicly:

export const PROMISE_REDUCER_STATE_IDLE = 'idle';
export const PROMISE_REDUCER_STATE_PENDING = 'pending';
export const PROMISE_REDUCER_STATE_SUCCESS = 'success';
export const PROMISE_REDUCER_STATE_ERROR = 'error';

export const PROMISE_REDUCER_STATES = [
  PROMISE_REDUCER_STATE_IDLE,
  PROMISE_REDUCER_STATE_PENDING,
  PROMISE_REDUCER_STATE_SUCCESS,
  PROMISE_REDUCER_STATE_ERROR,
];

export const PROMISE_REDUCER_ACTION_START = 'start';
export const PROMISE_REDUCER_ACTION_RESOLVE = 'resolve';
export const PROMISE_REDUCER_ACTION_REJECT = 'reject';
export const PROMISE_REDUCER_ACTION_RESET = 'reset';

const promiseInitialState = { state: PROMISE_REDUCER_STATE_IDLE, valueOrError: null };
export function promiseReducer(state = promiseInitialState, actionType, valueOrError) {
  switch (actionType) {
    case PROMISE_REDUCER_ACTION_START:
      return { state: PROMISE_REDUCER_STATE_PENDING, valueOrError: null };
    case PROMISE_REDUCER_ACTION_RESOLVE:
      return { state: PROMISE_REDUCER_STATE_SUCCESS, valueOrError: valueOrError };
    case PROMISE_REDUCER_ACTION_REJECT:
      return { state: PROMISE_REDUCER_STATE_ERROR, valueOrError: valueOrError };
    case PROMISE_REDUCER_ACTION_RESET:
      return { ...promiseInitialState };
    default:
      return state;
  }
}

export function extractPromiseStateEnum(promiseState = promiseInitialState) {
  return promiseState.state;
}
export function extractPromiseStarted(promiseState = promiseInitialState) {
  return (promiseState.state === PROMISE_REDUCER_STATE_PENDING);
}
export function extractPromiseSuccess(promiseState = promiseInitialState) {
  return (promiseState.state === PROMISE_REDUCER_STATE_SUCCESS);
}
export function extractPromiseSuccessValue(promiseState = promiseInitialState) {
  return (promiseState.state === PROMISE_REDUCER_STATE_SUCCESS ? promiseState.valueOrError || null : null);
}
export function extractPromiseError(promiseState = promiseInitialState) {
  return (promiseState.state === PROMISE_REDUCER_STATE_ERROR ? promiseState.valueOrError || true : null);
}

It's not this reducer user's concern whether what's being stored is state, valueOrError or something else. Exposed are the state string (enum), a couple frequently used checks on that state, the value and the error.

I have come across "selector spaghetti" however, where nested selectors for every trivial sub-piece of state caused quite the confusion.

If this nesting was caused by mirroring the reducer nesting (composition), that's not what I'd recommend, that's that reducer's implementation detail. Using the above example, the reducers that use promiseReducer for some parts of their state export their own selectors named according to these parts. Also not every function that looks like a selector has to be exported and be part of the reducer API.

function extractTransactionLogPromiseById(globalState, transactionId) {
  return extractState(globalState).transactionLogPromisesById[transactionId] || undefined;
}

export function extractTransactionLogPromiseStateEnumByTransactionId(globalState, transactionId) {
  return extractPromiseStateEnum(extractTransactionLogPromiseById(globalState, transactionId));
}

export function extractTransactionLogPromiseErrorTransactionId(globalState, transactionId) {
  return extractPromiseError(extractTransactionLogPromiseById(globalState, transactionId));
}

export function extractTransactionLogByTransactionId(globalState, transactionId) {
  return extractPromiseSuccessValue(extractTransactionLogPromiseById(globalState, transactionId));
}

Oh one more thing that I almost forgot. Function names and exports/imports can be minified well and safely. Nested object keys ā€” not so much, you need proper data flow tracer in the minifier to not screw up the code.

@timotgl : a lot of our encouraged best practices with Redux are about trying to encapsulate Redux-related logic and behavior.

For example, you can dispatch actions directly from a connected component, by doing this.props.dispatch({type : "INCREMENT"}). However, we discourage that, because it forces the component to "know" it's talking to Redux. A more React-idiomatic way to do things is to pass in bound action creators, so that the component can just call this.props.increment(), and it doesn't matter whether that function is a bound Redux action creator, a callback passed down by a parent, or a mock function in a test.

You can also hand-write action types everywhere, but we encourage defining constant variables so that they can be imported, traced, and decrease the chance of typos.

Similarly, there's nothing preventing you from accessing state.some.deeply.nested.field in your mapState functions or thunks. But, as has already been described in this thread, this increases the chances of typos, makes it harder to track down places that a particular piece of state is being used, makes refactoring more difficult, and means that any expensive transformation logic is probably re-running every time even if it doesn't need to.

So no, you don't _have_ to use selectors, but they're a good architectural practice.

You might want to read through my post Idiomatic Redux: Using Reselect Selectors for Encapsulation and Performance.

@markerikson I'm not arguing against selectors in general, or selectors for expensive computations. And I never intended to pass dispatch itself to a component :)

My point was that I disagree with this belief:

Ideally, only your reducer functions and selectors should know the exact state structure, so if you change where some state lives, you would only need to update those two pieces of logic.

About your example with state.some.deeply.nested.field, I can see the value of having a selector to shorten this. selectSomeDeeplyNestedField() is one function name vs. 5 properties I could get wrong.

On other hand, if you follow this guideline to the letter, you're also doing const selectSomeField = state => state.some.field; or even const selectSomething = state => state.something;, and at some point the overhead (import, export, testing) of doing this consistently doesn't justify the (debatable) safety and purity anymore in my opinion. It's well-meant but I can't shake off the dogmatic spirit of the guideline. I'd trust the developers in my project to use selectors wisely and when applicable - because my experience so far has been that they do that.

Under certain conditions I could see why you would want to err on the side of safety and conventions though. Thanks for your time and engagement, I'm overall very happy we have redux.

Sure. FWIW, there are other selector libraries, and also wrappers around Reselect as well. For example, https://github.com/planttheidea/selectorator lets you define dot-notation key paths, and it does the intermediate selectors for you internally.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mickeyreiss-visor picture mickeyreiss-visor  Ā·  3Comments

ms88privat picture ms88privat  Ā·  3Comments

amorphius picture amorphius  Ā·  3Comments

vslinko picture vslinko  Ā·  3Comments

vraa picture vraa  Ā·  3Comments