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

Update flow-bin to 0.91.0 #731

Merged
merged 20 commits into from
Feb 1, 2019
Merged

Update flow-bin to 0.91.0 #731

merged 20 commits into from
Feb 1, 2019

Conversation

glasserc
Copy link
Contributor

Go big or go home.

Replaces #699.

greenkeeper bot and others added 18 commits January 31, 2019 17:49
This has to be an exact object and corresponds to what is returned by
mapStateToProps.

We also have to export the `Props` type, which is the type of the
"connected" props, with state and dispatch functions connected up by
the container.

The functions in `Props` correspond to action
dispatchers (a.k.a. "action creators"). Previously, we explicitly
named the actions that the component could dispatch, which is kind of
cool. Let's keep that, but explicitly re-use the notification actions
from the `actions/Notifications.js`, instead of retyping their type
signature.

Previously, these actions were typed as returning void, but actually
they return the same action that's being dispatched. We ignore the
returned action so just use "*" where needed so that it will
typecheck.
- It helps flow figure out what's going on if `mapStateToProps`
  has an explicit return type.

- `DispatchAPI` is generic (takes "any action"). Express this using
  "*". This introduces a little bit of unsafety because we could ask
  it to dispatch non-action things, but fixing this requires exporting
  a valid type from `actions/notifications.js`, which is a bear
  because of facebook/flow#7377.

- `connect` is now generic and takes `Props`, `OwnProps`,
  `StateProps`, `DispatchProps`, and two other things I never bothered
  to look up. However, Flow is smart enough to infer most of these
  when you put `_`. Unfortunately, `eslint` gets upset because `_` is
  undefined, so disable linting on this line.

To elaborate on the type parameters to `connect`,

- `Props` is the set of props actually used by the "raw" component. It
  is an amalgamation of some of the other type parameters given here.

- `OwnProps` is the set of props that are passed to the connected
  component. In this particular case, we don't pass any props to the
  component, so pass an exact empty object.

- `StateProps` is the set of props extracted using `mapStateToProps`.

- `DispatchProps` is a set of "action creators" created using
  `mapDispatchToProps`. This should also be an "exact object type",
  and it happens that the `actions/notifications.js` module defines
  one. It happens that the "dispatching" action creators return the
  same actions as the functions from that module, so we can just
  re-use the type.
This one is a bit trickier than Notifications because it has
OwnProps (from where it's called in routes.js).

The OwnProps must be an exact object, like StateProps and
DispatchProps, so we can't pass extra props. The props given by Route
includes match, location, and history, none of which are used by App.
This actually caught a bug -- capabilities was being used without
being passed.
In order to make the typings line up, I re-used the signature for
deleteRecord. Unfortunately, we seem not to always have an `rid`
present, so special case that. We'll have to revisit.

Additionally, not all calls to `deleteRecord` pass a `last_modified`,
so make it optional. In the underlying kinto-http layer, it is, but
it shouldn't be being called in situations where it's missing, so
we'll have to revisit this too.

I also understood that flow has some confusing behaviors with
intersections of object types, so just convert stuff to exact objects
and combine them using spread. I'm not really sure why this works but
it seems to.

This also corrects a bug in the typings that isn't actually a real bug
-- deleteRecord and redirectTo are missing from the props passed to
the component (although they're actually being passed).
These are all linked by the HistoryTable, so do them all at once.

This fixes some typing errors:

- For some reason, many of these components said they needed the
  bucket, but didn't actually use it.

- For some reason, these containers all passed an updatePath function,
  though none of them needed it.
This affects both the BucketCollections and CollectionRecords
components. We already committed the CollectionRecords component, so
just include BucketCollections here.

While we're here, clean up the typings of the BucketCollectionsPage
container, which needs much fewer actions than it passes.
These are internal and not connected, so their actions can generally
have just about any type signature. In particular, many of the forms
only *sometimes* call the associated action, waiting first for a
confirm().
The data we provide to "update" functions includes at least one of
data and permissions but not necessarily both. This makes it unlike
the data you get back for those resources. Formalize this in the types
of some actions and use them to fix the Attributes and Permissions
components that use them.

While we're here, tighten up some of the Record containers
types (passing more actions than we need again).
While we're here, tighten up a few more types -- sometimes
Capabilities is passed but not used, and sometimes updatePath is
passed but not needed.
In order to get the StateProps to line up, I had to correct an error
where the history field is declared to be both an array of strings and
an array of history entries. I'm not a Redux expert but it seems like
the history field is managed by the history reducer, which maintains a
state which is an array of history entries, so this means that this
probably wasn't a real error.

Despite getting errors that the StateProps were wrong, I didn't get
any errors complaining that the history reducer state was incompatible
with the app state. I suspect the typing isn't perfect yet, which is a
little scary, but that's JavaScript.
This module contains wrappers around react-router stuff that tries to
extract breadcrumb information and fire route-updated
messages. Because react-router is higher-order, it's a bit
trickier. The solution this PR pushes towards isn't really complete or
the cleanest, but it gets us to a passing build and I'm running out of
time to work on this.

`routeCreator` is a wrapper around `Route` that is also a connected
component, so we have to define its prop types. I don't see any
obvious way to get it to automatically infer the props that `Route`
uses and the type is not exported so we redefine it here, more or
less.

`Route` does not take `title` so be careful not to pass it in the
props that go to `Route`.

`Route` is a higher-order-component that wraps our component (using
`render`). Our components are all connected components so they don't
get any particular props, but they all get the React Router
props (match, location, and history) which are defined in
`ContextRouter`.

We also insert a `ComponentWrapper` which wraps our component. This is
what we actually give to `Route`. It gets all the same router props
plus a component to call and the connected onRouteUpdated function.
However, let's still try not to pass `routeUpdated` to the component.
@glasserc
Copy link
Contributor Author

OK, so I think this actually typechecks. I cheated a little bit at the end -- check src/routes.js for the gory details. I think it should be possible to type this correctly, but it means doing another pass over every connected component and I didn't think it was worth it at this point in time. But I could probably do it if you wanted.

@glasserc
Copy link
Contributor Author

Also, some helpful references:

I also understand redux a lot better now..

Copy link
Contributor

@leplatrem leplatrem left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Amazing 😮

I read the whole story (was gonna say saga) in the individual commits and everything makes sense.
Just the connect() functions look a bit weird, but bearable with your commit comments (I don't know if it makes sense to copy them into the code base...)

Congrats on the amount of work done here. Let's pray for a v1.0 of flow soon...

// though `Route` always passes the complete set of router
// props. We can't reproduce this behavior in correctly-typed
// code, so let's not even try.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😓 oh man... #souffrance

};
}

function mapDispatchToProps(dispatch: Dispatch): ActionCreatorOrObjectOfACs {
// FIXME: component doesn't need NotificationsActions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does it mean?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, I thought I had cleaned these all up. If you look at the RecordCreate component, its Props indicates that it only needs createRecord, not any of the other stuff it's being connected to. I'll fix this in another commit.

return bindActionCreators(NotificationsActions, dispatch);
}

export default connect(
export default connect<Props, {||}, _, _, _, _>( // eslint-disable-line
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the eslint thing something we should track to remove them once eslint will support the new flow-bin?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess so. I opened gajus/eslint-plugin-flowtype#384.

@glasserc
Copy link
Contributor Author

glasserc commented Feb 1, 2019

I'm not sure if it's worth adding code comments to the connect type signature. By the fifth time you read https://github.com/flow-typed/flow-typed/blob/master/definitions/npm/react-redux_v5.x.x/flow_v0.89.x-/react-redux_v5.x.x.js, it mostly starts to make sense..

Copy link
Contributor

@leplatrem leplatrem left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👏 👏 👏

@glasserc glasserc merged commit 44f547e into master Feb 1, 2019
@glasserc glasserc deleted the greenkeeper/flow-bin-0.91.0 branch February 1, 2019 16:19
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

Successfully merging this pull request may close these issues.

2 participants