New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[react-redux_v5.x.x] Connect the Dispatch type with actions in dispatch params
#3035
[react-redux_v5.x.x] Connect the Dispatch type with actions in dispatch params
#3035
Conversation
| @@ -52,6 +52,8 @@ declare module "react-redux" { | |||
| // and provide the StateProps type to the SP type parameter. | |||
| | ((state: S, ownProps: OP) => (state: S, ownProps: OP) => SP); | |||
|
|
|||
| declare type Bind<D> = <A, R>((...A) => R) => (...A) => $Call<D, R>; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is what bindActionCreators() actually does.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the corresponding change be made to redux libdef as well?
| options?: ?Options<S, OP, {||}, {| ...OP, ...DP |}>, | ||
| // Got error like inexact OwnProps is incompatible with exact object type? | ||
| // Just make your OP parameter an exact object. | ||
| ): Connector<P, OP, {| ...OP, ...$ObjMap<DP, Bind<D>> |}>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here is the main piece of code in this PR:
$ObjMap<DP, Bind<D>>In simple case it just make sure that the actions returned by the specified in DP action creators are compatible with the function type in D. That it's a function type is implicitly specified by the usage of $ObjMap.
What's more interesting, in case of redux-thunk the code above transforms the raw thunks of type (...A) => (...B) => R from mapDispatchToProps object to (...A) => R in the resulting Props. This thing makes it possible to use thunks returning something useful:
const thunkB = (id) => async (dispatch: Dispatch) => {
const data = await fetchTodo(id);
dispatch(todoLoaded(data));
}
const thunkA = async (dispatch: Dispatch) => {
// dispatch returns the promise from thunkB
await dispatch(thunkB('todo123'))
}
class TodoLoader ... {
componentDidMount() {
const { thunkA } = this.props;
// so it does in the props too and the new typings support this
thunkA.then(trackTodoLoaded);
}
}| // ... and here the property returns a return value of thunk | ||
| // as dispatch calls it for us with `dispatch` and `getState` | ||
| thunk: () => Promise<number>, | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a test case for redux-thunk as described in previous comment. Here you can see that DispatchProps type as returned from mapDispatchToProps is different to what the Props get. This is exactly because of what connect() does: it hides the arguments by wrapping the thunk value.
Btw, can it be considered as a good monad example, dear functional gurus? 🤓
| )(Com); | ||
| e.push(Connected); | ||
| <Connected passthrough="foo" />; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think, is it time to split the tests for connect in multiple files by topic? Like test_connectMergeProps.js, test_connectStateOnly.js, test_connectConfig.js.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that could make sense! Good idea 👍
|
This looks great. Massive thanks for all the work you've been doing on these typings! I think there is one overload of In our code, at least, this seems to be the most commonly used overload. I've taken a shot at typing these in our code based on your work and they seem to work well so far: |
|
@EugeneZ 🤦♀️ yes, you're right. It was not exactly forgotten, but embedded into the function case and thus typed incorrectly in case of thunks. Thanks a lot for trying out the yet very raw material! And happy new year 🎄🎅🍋! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea to split to files, and one (apparently?) typo :)
| @@ -548,4 +548,140 @@ function testHoistConnectedComponent() { | |||
| <Connected passthrough={123} passthroughWithDefaultProp={456} forMapStateToProps={'data'}/>; | |||
| // OK with declared static property | |||
| Connected.myStatic; | |||
| //$ExpectError property `myStatic1` is missing in statics | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you mean notStatic? :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Eagle eye achievement unlocked!
| )(Com); | ||
| e.push(Connected); | ||
| <Connected passthrough="foo" />; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that could make sense! Good idea 👍
And also fix type arguments order for mergeProps version
a1d6edc
to
9ea1430
Compare
|
Rebased to hopefully get green tests. Will split into files asap. |
Also removed type parameters and added a clarifying comment.
|
Nice, GJ! |
Long time no see :)
This PR solves the problem with actions in dispatch props have currently no connection to the
Dispatchtype argument of theconnect()function. In practice this means that Flow will not stop us from returning any random type from an action creator given inmapDispatchToProps. Here is an example:We do check the
mapStateToPropsto get the real state type (if it's present in the code and given or inferable from the call) but fo dispatch theDispatchtype is just bogus and was added to be typed in the bright future. Hopefully, it is now :)Also, I've fixed some minor things:
configandmergePropsarguments if nomapStateToPropsparameter passed;mergePropspart of typings (was a typo);{||}asstatePropstomergePropsandconfiginstead of wrongly inferredemptytype for the non presentStatePropsandDispatchProps.As this PR makes the typings significantly stricter (and safer) but a bit harder to drop into a random project (especially, after the habits we've developed with the very forgiving mode of Flow <v0.85) we should discuss if and how to make this change smooth 😄