Search code examples
javascriptreactjsreduxredux-thunk

React-Redux: Where do I put a side effect that uses part of a reducer?


I know not to put side effects in reducers, and I know there are lots of great explanations about how to handle async actions. I have read them. I have a specific question I'm stumped on. Thanks!

I have state.largeObject which is an object with many entries. I have a reducer that does some complex logic and merges the result into state.largeObject like so:

export const myReducer = (state, { input }) => {
  const largeObject = doSomethingComplex(input)
  // other logic that uses largeObject

  return {
    ...state,
    largeObject: {
      ...state.largeObject,
      ...largeObject
    }
  }
}

I want to save the result of doSomethingComplex(input) to the server. Where do I put the side effect? EDIT: without duplicating doSomethingComplex which is still needed for other logic in the reducer.

  • I can't put it in myReducer or doSomethingComplex since they are pure.
  • I can't put it in Redux middleware, as that only has access to the action and the state as a whole. I don't want to call doSomethingComplex both in the reducer and in the middleware.
  • I don't want to move it into an action-creator, since that would force a lot of pure functionality into a thunk, making it harder to compose.

What am I missing? Thanks!


Solution

  • Middleware, thunks, and store subscription callbacks are all valid places to do that:

    • It's fine to have some saving logic as part of a thunk. Thunks are a type of middleware, and that's where side effects are supposed to live in general.
    • You could have a custom middleware that looks for the specific action type that results in this change, or checks to see if this chunk of state has changed, and makes a call to the server after the state has been updated
    • You could have a store subscribe callback that checks to see if this chunk of state has changed and makes a call to the server

    Based on the way you phrased things, I'm not sure if you're looking to send only the result of that call to the server, or the result of doing {...state.largeObject, ...largeObject}.