Skip to content
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

[Flow 0.85] How to type connected components with react-redux? #7125

Closed
atabel opened this issue Oct 31, 2018 · 12 comments
Closed

[Flow 0.85] How to type connected components with react-redux? #7125

atabel opened this issue Oct 31, 2018 · 12 comments

Comments

@atabel
Copy link
Contributor

atabel commented Oct 31, 2018

As announced in this blog post, Flow 0.85 needs extra type annotations for some exports.

This seems to be the case for react-redux connected components.

Here is an example:
https://flow.org/try/#0PQKgBAAgZgNg9gdzCYAoVBLAtgBzgJwBdkwBDAZzACUBTUgY2KnzizAHJ87H2BudYMDDlsOGBigYaAEzBcGhALRdpAVwAeYQgE8cNcumk16MUly26aYACIZyOUoXoALAIIAFAJIAeVwD4wAF4wAAoFDDgAOwAuMFcASiCA134jEzMrHT0bOwcnZ19YgG8smliAEgBlVQAjUu9yQnwMSIBzPwBfAODbe0cXDx9-flQ003NSsABZUhxKwkcaABU4dxYccm9K2IB5GoArY0IAGjBK912Do9Oqc8vDxm7QxsXYytOcdfI390TAgNu7hGYwyYCgqkijAikTA9CikSO3lQYDAAGFWLFaAoAHTo3BRGiRQhLSzeEB+Y7Is73a5Uu5gPYPE5UwE0xiUlGoi5gcq2KBQbxYxjYgCiMBoWEJhHRkUkrW8eIp1HOFLpS2KAG0APoVADSNG0m0VAF1YqRItoOqg-CEqVhZvNFis1nANrEZnMFoRlqsvltTucbiqOWB7ThenkXM6vgB+WKRVQwGCoeKxEJw-EIomxPF-AHcQi41h4LPE0lcsAAMjO7gC1aqzlmNC2Sz8I0EYEJsjgUDkBeUMg0Fj0BnQkxdGyCYCKAB8qTh46osDUaPgQy0mTJuQnl6vUDOOiM4ZFGmjiwSiVPQkUcKcN0ctx1YhPyHnQlTvNIMAA3Pw3jpkJEshFPejCPt4wBfr+YApugx6nmGjretGrqUMEIRviERRUqB3rSNyADMqAdPER5RKeMoImB0h4jgU7HlRhAhIhXo+i+8QhLRF6EKR6AhBhSTvii3h+FSwlcaWYA4IERQAIwAbhW4yQATABwCiSi4nwg+NHFlJMnyWA6kfsZ8QcfE6A0OoeBEGARhQKQibEJROm0fwmkeZ5XneWAQA

It fails with these two errors in Flow 0.85, but worked fine in Flow 0.84

Missing type annotation for `CP`. `CP` is a type parameter declared in function type [1] and was implicitly instantiated at call of `connect` [2].

and

Missing type annotation for `SP`. `SP` is a type parameter declared in function type [1] and was implicitly instantiated at call of `connect` [2]

So what's the recommended way to type connected components with react-redux and Flow 0.85?

@atabel
Copy link
Contributor Author

atabel commented Oct 31, 2018

Not sure if I should open an issue to flow-typed instead

@samwgoldman
Copy link
Member

See my response to this question on the Medium post.

The simplest thing is to add an explicit type argument to the call of connect, which is at the location pointed to by the error message in your example. The specific types you provide depend on the signature of the function.

I suspect that the flow-typed folks might want to rework the type declarations to make it simpler to provide these annotations. I suggested one approach in my comment on Medium. The details of the specific declaration should be taken up with the author, so I suspect that a question to the flow-typed repo would be a good step.

@peter-leonov
Copy link
Contributor

@samwgoldman, thanks a bunch for providing those concise and strict typings for connect I personally was missing forever!

Imho, there is a little typo in the typings:

-  mapStateToProps: MapStateToProps<S, OP, DP>,
+  mapStateToProps: MapStateToProps<S, OP, SP>,
-  mapDispatchToProps: MapDispatchToProps<D, OP, SP>,
+  mapDispatchToProps: MapDispatchToProps<D, OP, DP>,

This fixes an error like this:

[Flow] Cannot call connect with mapStateToProps bound to mapStateToProps because property someDispatchProperty is missing in StateProps but exists in DispatchProps [2] in the return value.

@peter-leonov
Copy link
Contributor

peter-leonov commented Nov 4, 2018

By the way, for those type hackers spending their weekends in fixing 100 new (legitimate) errors, here is how I use a bit modified version of Sam's typings.

First I split the huge all in one Props type in three distinct types (OwnProps, StateProps, DispatchProps) and one combined (just Props):

// these are the old school props coming from component usage:
// <Component own1="foo" own2="bar" />
type OwnProps = {|
  own1: string,
  own2: number,
|};

// these are the props coming from state via `mapStateToProps`
type StateProps = {|
  state1: boolean,
  state2: number,
|};

// these are the props dispatching actions from `mapDispatchToProps`
type DispatchProps = {|
  action1: typeof actionCreator1, // actionCreator1 is an imported action creator function
  action2: typeof actionCreator2, // actionCreator2 is an imported action creator function
|};

// and finally the main type for component type creation (`Component<Props, …>`, see below)
type Props = {|
    ...OwnProps,
    ...StateProps,
    ...DispatchProps,
|};

// here is the normal React component class definition
class MyComponent extends Component<Props, void> {
  render() {
    // `this.props` has all the props as expected because it's of type `Props`
    console.log(this.props.own1)
    console.log(this.props.own2)
    console.log(this.props.state1)
    console.log(this.props.state2)
    // including action dispatchers
    console.log(this.props.action1())
    console.log(this.props.action2())
  }
}

// and the most important part is using the `connect()` function with proper type parameters
export const MyController = connect<State, Dispatch, OwnProps, StateProps, DispatchProps>(
    mapStateToProps,
    mapDispatchToProps,
)(MyComponent);

The State and Dispatch types come from how it's officially recommended to type Redux with Flow. In short:

// the overall state of your app
type State = {|
  +forms: FormsReducerState,
  +notifications: NotificationsReducerState,
  // and so on
|}

// all the actions of your app
type Action = FormsAction1 | FormsAction2 | NotificationsAction1 | NotificationsAction2;

// type of a `dispatch()` function
type Dispatch = Action => Action

P.S. Adding this code to connect() typings allows to use an object with dispatchers instead of a function:

+ declare export function connect<S, D, OP, SP, DP>(
+     mapStateToProps: MapStateToProps<S, OP, SP>,
+     mapDispatchToProps: DP,
+ ): Connector<S, D, OP, React$ComponentType<{| ...OP, ...SP, ...DP |}>>;

Notice, that I use a bit modified version of Sam's typings. Patches are welcome :)

EDIT: as stated in Sam's comment it is also possible to use _ type placeholder in place of StateProps and DispatchProps arguments in the call to connect() to make Flow infer the types for you:

export const MyController = connect<State, Dispatch, OwnProps, _, _>(
    mapStateToProps,
    mapDispatchToProps,
)(MyComponent);

but his trick makes error messages a bit more verbose and possible make Flow less performant. Something like how it was with the Existential Type (*).

@laurenskling
Copy link

laurenskling commented Nov 6, 2018

Whenever I think I'm starting to get flow, stuff like this breaks my brain 😅
I have no idea how to fix my 300 connect errors right now. Giving them 6 parameters doesn't really sound like the most readable thing to do.

@peter-leonov
Copy link
Contributor

I wonder if the should be a way to tell Flow "I know what I'm doing" and make it possible to export a generic instantiation, like $FlowFixMe but for performance related things. Kinda $FlowTrustMe :D

10xjs added a commit to sensu/sensu-go that referenced this issue Nov 28, 2018
see: facebook/flow#7125

Signed-off-by: Neal Granger <neal@nealg.com>
10xjs added a commit to sensu/sensu-go that referenced this issue Nov 28, 2018
see: facebook/flow#7125

Signed-off-by: Neal Granger <neal@nealg.com>
@kangax
Copy link

kangax commented Dec 10, 2018

My sad face when Flow is already at 0.88 but we can't get past 0.85 without $FlowFixMe-fying 500 errors...

@oshalygin
Copy link

This feels pretty bad. Why was this closed?

@peter-leonov
Copy link
Contributor

peter-leonov commented Jan 16, 2019

@oshalygin I guess, because of these two PRs to flow-typed: flow-typed/flow-typed#3012 and flow-typed/flow-typed#3035, we continued the party there.

@peter-leonov
Copy link
Contributor

We are also crafting another one to make the connect() function typings more composable with other HOC (in terms of compose() function from Redux) here: flow-typed/flow-typed#3076. Please, help 😊

@oshalygin
Copy link

Thanks for the update @peter-leonov !

@vaukalak
Copy link

@kangax As far as I understand it should be still possible to type via:

connect<*, *, *, *, *>

So I think, running replace of connect( with connect<*, *, *, *, *>( may solve part of your issues.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants