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

Add flow to SyntheticEvent #19564

Merged
merged 4 commits into from Aug 19, 2020
Merged

Conversation

eps1lon
Copy link
Collaborator

@eps1lon eps1lon commented Aug 8, 2020

Summary

Type-check SyntheticEvent and unsound usage.

According to the types there were a lot of unnecessary guards against event._reactName being null. Removing all these checks does not fail any tests but produces unsound types. The origin of _reactName being null is

topLevelEventsToReactNames.set(type, null);

Test Plan

  • CI green
  • add runtime test for _reactName === null?

@codesandbox-ci
Copy link

codesandbox-ci bot commented Aug 8, 2020

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 6d4d97f:

Sandbox Source
React Configuration

@sizebot
Copy link

sizebot commented Aug 8, 2020

No significant bundle size changes to report.

Size changes (stable)

Generated by 🚫 dangerJS against 6d4d97f

@sizebot
Copy link

sizebot commented Aug 8, 2020

No significant bundle size changes to report.

Size changes (experimental)

Generated by 🚫 dangerJS against 6d4d97f

@@ -923,7 +923,7 @@ function accumulateEnterLeaveListenersForEvent(
inCapturePhase: boolean,
): void {
const registrationName = event._reactName;
if (registrationName === undefined) {
if (registrationName === null) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Right now this can never be true:

It might make sense to add an invariant or just cast it to a string with a hint to the current data flow.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't understand this change.

If it was previously sometimes undefined, then this changes behavior.

If it was previously sometimes null, then this changes behavior.

If it was neither, then this line can be removed, and the type can be made more strict.

Does that make sense?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think maybe you're saying that the type is more loose but for this particular case, we only get a subtype? Can we codify that in types?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

If it was previously sometimes undefined, then this changes behavior.

Not according to the flow types. Maybe there are still some files where SyntheticEvent is used that are uncovered?

If it was previously sometimes null, then this changes behavior.

It does change but there weren't any test so we're definitely missing some code coverage. This is what I meant with adding a test for reactName === null.

I think maybe you're saying that the type is more loose but for this particular case, we only get a subtype? Can we codify that in types?

Exactly. I'll try something like UnknownSyntheticEvent { reactName: null } and KnownSyntheticEvent { reactName: string } and see if we can get rid of some checks with earlier type checks.

Copy link
Collaborator

Choose a reason for hiding this comment

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

My impression is that this branch has something to do with the createEventHandle API. But I might be wrong. It would be good to look in git for why it was added.

Copy link
Collaborator Author

@eps1lon eps1lon Aug 13, 2020

Choose a reason for hiding this comment

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

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It would be good to look in git for why it was added.

991c3b8#diff-6b784480f6f3297c42bc2e6e46e6f943L829-R818

event.dispatchConfig.registrationName was undefined | string while reactName is assumed to be string (even though it was nullable and still is.

@eps1lon eps1lon marked this pull request as ready for review August 13, 2020 18:13
@eps1lon eps1lon force-pushed the test/null-react-name branch 2 times, most recently from d1b61e6 to e75f163 Compare August 13, 2020 19:27
enterEventType,
eventTypePrefix + 'enter',
to,
nativeEvent,
nativeEventTarget,
eventInterface,
);
enter.target = toNode;
enter.relatedTarget = fromNode;
// flow thinks `enter` could be `null` at this point
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why

Copy link
Collaborator Author

@eps1lon eps1lon Aug 13, 2020

Choose a reason for hiding this comment

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

We have to declare it as let enter: KnownReactSyntheticEvent | null because we later re-assign it to null conditionally:

if (nativeTargetInst !== targetInst) {
enter = null;
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'm not familiar with flow so there might be a better alternative. The current statements seems sound to me. TypeScript is fine with it, but flow is not

Copy link
Collaborator

Choose a reason for hiding this comment

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

You could have two variables. First one is not nullable and that’s where you initialise. Then you copy it to a second nullable one.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Took the liberty and flipped the assignment around. Instead of conditionally nulling we conditionally create the event. This means no more wasted work in the constructor.

Otherwise we'll end up with

let enter: SyntheticEvent | null = null;
const enterEvent: SyntheticEvent = { reactName: 'mouseleave' }
enterEvent.reactName = '';
enter = enterEvent

which looks odd. Or do you prefer that?

// If we are not processing the first ancestor, then we
// should not process the same nativeEvent again, as we
// If we are processing the first ancestor, then we
// should process the same nativeEvent again, as we
Copy link
Collaborator

@gaearon gaearon Aug 19, 2020

Choose a reason for hiding this comment

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

This wording change makes the comment nonsensical. I'll reword.

@gaearon gaearon merged commit 87b3e2d into facebook:master Aug 19, 2020
@gaearon
Copy link
Collaborator

gaearon commented Aug 19, 2020

Thanks!

@eps1lon eps1lon deleted the test/null-react-name branch August 19, 2020 13:23
koto pushed a commit to koto/react that referenced this pull request Jun 15, 2021
* Add flow to SyntheticEvent

* Minimal implementation of known and unknown synthetic events

* less casting

* Update EnterLeaveEventPlugin.js

Co-authored-by: Dan Abramov <dan.abramov@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants