Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to bind async actions creators using flowtype?

I just started learning flowtype and i need some help to understand two things that aren't clear on my mind.

  1. Using https://github.com/reactjs/redux/blob/master/examples/todos-flow as an example, i wonder how control over types can work without type definitions of https://github.com/flowtype/flow-typed, in this case: https://github.com/flowtype/flow-typed/blob/master/definitions/npm/redux_v3.x.x/flow_v0.33.x-/redux_v3.x.x.js ?

  2. If I use redux definitions, the validation of bindActionCreators fails when i try to bind async action creator (i'm using redux-thunk).

How to continue using flow and bind async actions creators when using redux-thunk?

Code sample (https://gist.github.com/momsse/323c228e8c5e264067039b8446cd890f) :

import { bindActionCreators } from 'redux';
import type { Dispatch } from 'redux';

type Action = { type: 'SET_PROFILE', profile: Object };

/**
 * Based on https://github.com/gaearon/redux-thunk/blob/master/index.d.ts
 */
type ThunkAction = (dispatch: Dispatch<Action>,
                    getState: () => any,
                    extraArgument: any) => any;

type Profile = {
  name: string,
  team: string
}

// Async actions creator
function setProfile(profile: Profile): ThunkAction {
  return dispatch => setTimeout(() => dispatch({ type: 'SET_PROFILE', profile }), 2000);
}

const profileActionCreators = { setProfile };

type Props = {
  actions: {
    setProfile: (profile: Profile) => ThunkAction,
  }
}

function mapDispatchToProps(dispatch: Dispatch<Action>): Props {
  return {
    actions: bindActionCreators(profileActionCreators, dispatch)
  };
}

Errors:

 40:     actions: bindActionCreators(profileActionCreators, dispatch)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function call. Function cannot be called on any member of intersection type
 40:     actions: bindActionCreators(profileActionCreators, dispatch)
                  ^^^^^^^^^^^^^^^^^^ intersection
  Member 1:
   49:   declare function bindActionCreators<A, C: ActionCreator<A, any>>(actionCreator: C, dispatch: Dispatch<A>): C;
                                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ polymorphic type: function type. See lib: flow-typed/npm/redux_v3.x.x.js:49
  Error:
   49:   declare function bindActionCreators<A, C: ActionCreator<A, any>>(actionCreator: C, dispatch: Dispatch<A>): C;
                                                   ^^^^^^^^^^^^^^^^^^^^^ function type. Callable signature not found in. See lib: flow-typed/npm/redux_v3.x.x.js:49
   40:     actions: bindActionCreators(profileActionCreators, dispatch)
                                       ^^^^^^^^^^^^^^^^^^^^^ object literal
  Member 2:
   50:   declare function bindActionCreators<A, K, C: ActionCreators<K, A>>(actionCreators: C, dispatch: Dispatch<A>): C;
                                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ polymorphic type: function type. See lib: flow-typed/npm/redux_v3.x.x.js:50
  Error:
   13:   declare type Dispatch<A: { type: $Subtype<string> }> = (action: A) => A;
                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^ property `type` of object type. Property not found in. See lib: flow-typed/npm/redux_v3.x.x.js:13
   21: function setProfile(profile: Profile): ThunkAction {
                                              ^^^^^^^^^^^ function type
like image 295
momsse Avatar asked Nov 15 '16 22:11

momsse


1 Answers

These are the full declarations for ActionCreator and bindActionCreators:

  declare type ActionCreator<A, B> = (...args: Array<B>) => A;
  declare type ActionCreators<K, A> = { [key: K]: ActionCreator<A, any> };

  declare function bindActionCreators<A, C: ActionCreator<A, any>>(actionCreator: C, dispatch: Dispatch<A>): C;
  declare function bindActionCreators<A, K, C: ActionCreators<K, A>>(actionCreators: C, dispatch: Dispatch<A>): C;

In your code, bindActionCreators will wrap each property of profileActionCreators in dispatch. You seem to be expecting dispatch to be passed to the setProfile function, where dispatch can later be used as a callback.

But it doesn't seem to me that bindActionCreators supports "binding" dispatch as a callback. Rather, dispatch is "bound" like this:

function bindActionCreator(actionCreator, dispatch) {
  return (...args) => dispatch(actionCreator(...args))
}

So in your code, it effectively looks like this:

function mapDispatchToProps(dispatch: Dispatch<Action>): Props {
  return {
    actions: {
      setProfile: (profile) => dispatch(dispatch => setTimeout(() => dispatch({ type: 'SET_PROFILE', profile }), 2000)),
  };
}

Flowtype is therefore correctly catching the type error, saying that bindActionCreators expects each property of the object to be an actionCreator: () => Action.

You probably can't use bindActionCreators for your use case, or you need to rethink how you handle thunks. Here is an approach that should work.

const profileActionCreators = { setProfile };

type Props = {
  actions: {
    setProfile: (profile: Profile) => setTimeout,
  }
}

function mapDispatchToProps(dispatch: Dispatch<Action>): Props {
  const boundActions = bindActionCreators(
    profileActionCreators,
    dispatch
  );
  return ({
    actions: {
      setProfile: (profile: Profile) => setTimeout(() => boundActions.setProfile(profile), 2000),
    },
  });
}

Thunk approach

If you want to keep your ThunkAction approach, you won't be able to use bindActionCreators. This also works.

like image 83
thejohnbackes Avatar answered Sep 28 '22 01:09

thejohnbackes