jeffbski / redux-logic

Redux middleware for organizing all your business logic. Intercept actions and perform async processing.
MIT License
1.81k stars 107 forks source link

redux-logic + flow best practice? #13

Open ghost opened 7 years ago

ghost commented 7 years ago

Hi,

Currently I'm struggling with the use of Flow in combination with redux-logic because Flow does not understand that a given logic will only receive actions of a given type.

Given the following types:

export type Action =
          { type: 'SEARCH_LOCATION_SELECT', payload: string }
          | { type: 'GEOLOCATION_REQUEST' };

export type Dispatch = (action: Action) => any;

export type LogicContextT = {
  action: Action,
  getState: () => GlobalState
}

This is a problem for Flow:

const aroundMeSelectLogic = createLogic({
  type: 'SEARCH_LOCATION_SELECT',
  process({ action }: LogicContextT, dispatch: Dispatch): void {
    if (action.payload === 'AROUND_ME') {
      dispatch(geolocationRequest());
    }
  }
});

property payload. Property not found in object type

and this is NOT a problem for Flow:

const aroundMeSelectLogic = createLogic({
  type: '*',
  process({ action }: LogicContextT, dispatch: Dispatch): void {
    if (action.type === 'SEARCH_LOCATION_SELECT' && action.payload === 'AROUND_ME') {
      dispatch(geolocationRequest());
    }
  }
});

Does anyone have a suggestion on how to best handle this?

jeffbski commented 7 years ago

Would a work around be to define a payload for the other action too?

I'm sure there is a better way, but I wonder if that would work.

jeffbski commented 7 years ago

I believe I found a discussion of the same problem over in an issue on the redux repo, maybe there will be some ideas in there.

https://github.com/reactjs/redux/issues/290

jeffbski commented 7 years ago

It looks like this PR was the result of many discussions for the flow types for Redux. It has a union of actions, so maybe this provides some clues on how to proceed. I wish I understood Flow better to be able to provide some guidance, but I am not there yet.

ghost commented 7 years ago

Hi @jeffbski

Thanks for taking the time to reply.

Adding payload to all actions would not solve it since the payload might be different from action to action, it might solve the example I gave but then fail when I tried to access a property in a payload that was an object and not a string. Also, I might be listening for actions from 3rdparty libs which I don't control the action shape (eg. react-router-redux).

I am not sure what code you are exactly pointing to, but indeed a union of actions + polymorphic types can solve the issue, although things get a bit more verbose.

This is not a problem for Flow and is the best approach I can currently come up with :

export type SearchLocationSelectAction = { type: 'SEARCH_LOCATION_SELECT', payload: string };
export type GeolocationRequestAction = { type: 'GEOLOCATION_REQUEST' };

export type Action = SearchLocationSelectAction | GeolocationRequestAction;

export type Dispatch = (action: Action) => any;

export type LogicContextT<A> = {
  action: A,
  getState: () => GlobalState
}

const aroundMeSelectLogic = createLogic({
  type: 'SEARCH_LOCATION_SELECT',
  process({ action }: LogicContextT<SearchLocationSelectAction>, dispatch: Dispatch): void {
    if (action.payload === 'AROUND_ME') {
      dispatch(geolocationRequest());
    }
  }
});
jeffbski commented 7 years ago

@ivoanastacio Thanks for posting the polymorphic approach.

As I learn Flow better I'll post back any other ideas I come up with, but this seems to be the best way to go for the moment. I too wish it wasn't so verbose.