0

I'm replacing getState with an enhancer as follows:

interface ArtificialStateEnhancerProps {
  getArtificialStateGetter: StateGetterFn;
}

export const getArtificialStateEnhancer = ({
  getArtificialStateGetter
}: ArtificialStateEnhancerProps) => {
  return (createStore: typeof createStoreOriginal) => {
    return (rootReducer, preloadedState, enhancers) => {
      const store = createStore(rootReducer, preloadedState, enhancers);

      const { getState } = store;

      if (getArtificialStateGetter) {
        return { ...store, getState: getArtificialStateGetter(getState) };
      }
      return { ...store };
    };
  };
};

When using store.getState() somewhere in my code it works an my custom getStage method is used. However within an Action or Sage (using select()) the original, native getState from redux is used.

Example action:

export function myAction(
  slug: string
): ReduxAction<any> {
  return async (
    dispatch: Dispatch,
    getState: () => any // Here getState is used but the native version of redux
  ): Promise<any> => {
    const state = getState();

    const foo = ...do something with `state`
  };
}

Is this intended behavior?

Matthias Max
  • 595
  • 1
  • 7
  • 20

1 Answers1

0

It most likely depends on the ordering of where the middleware enhancer is added vs your custom enhancer.

If the middleware enhancer is added after the custom enhancer, then it is dealing with the base-level store methods. Each enhancer kind of "stacks" on top of each other.

My guess is that you've got something like:

compose(myCustomEnhancer, applyMiddleware(saga))

meaning that the middleware enhancer is after the custom one.

You'd need to flip those if that's the case.

(As a side question: could you give more details as to why you're writing a custom enhancer? It's a valid technique, but very rarely used, and it's even more rare that you would ever need or want to override getState.)

Also, note that we generally recommend against using sagas in almost all use cases, and especially if you're looking at using sagas for basic data fetching. Today, Redux Toolkit's "RTK Query" data fetching and caching API does a vastly better job of handling data fetching for you, and the RTK "listener" middleware handles the same reactive logic use case as sagas but with a simpler API, smaller bundle size, and better TS support.

See my talk The Evolution of Redux Async Logic for comparisons and explanations of why we recommend against sagas.

markerikson
  • 63,178
  • 10
  • 141
  • 157
  • Thanks for the reply @markerikson. Answers as a thread due to comment length restriction. Regarding the order you are right: I have this order: ```typescript composeEnhancers( getArtificialStateEnhancer({ getArtificialStateGetter: getArtificialStateGetterFn }), applyMiddleware(...initialMiddlewares) ) ``` Flipping the order, makes `getState` work as expected in `Actions`, `Sagas` and `store.getState()`. – Matthias Max Oct 20 '22 at 14:15
  • Now with this in place I think I just discovered my next faulty reasoning: Reducers will write to the "native" state. What I did though in my custom `getState` method is return a state passed in from the outside. It's a deep copy of the native state so matches it's structure etc. Which is why selectors etc still work. – Matthias Max Oct 20 '22 at 14:20
  • So, coming back to your side question: I'm implementing a kind of a "live editing" which is implemented using another state machine (built into the framework). It's faster than doing it on top of the existing redux setup and state. However actions and the result of their reducers are still expected to be applied to this "temporary" state. Because they are needed by the consuming connected components. And this is not the case with my custom enhancer. I think I'll have to re-think my whole strategy. – Matthias Max Oct 20 '22 at 14:23
  • I'm using `Sagas` like "hooks" to introduce business logic on certain actions. Would such a flow be covered without sagas ? I didn't check your article in depth but didn't see an exact example of a sage being replaced by another technique. Any pointers here would be appreciated. – Matthias Max Oct 20 '22 at 14:25
  • 1
    @MatthiasMax yeah, this is the wrong place to have a more detailed discussion. Could you come by the `#redux` channel in the Reactiflux Discord ( https://www.reactiflux.com ) ? We maintainers hang out there and answer questions - it'd be a lot easier to discuss details there. Also, per my answer / talk, the RTK 'listener" middleware is our recommended replacement for using sagas for reactive logic such as responding to specific actions. (also also I'm still sorta confused on the enhancer use case here :) ) – markerikson Oct 20 '22 at 23:45