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

Run <Slate />'s focus event listeners after <Editable />'s focus handlers in React >= 17 #4920

Merged
merged 4 commits into from Apr 3, 2022

Conversation

adri1wald
Copy link
Contributor

@adri1wald adri1wald commented Mar 31, 2022

Description
Run <Slate />'s focus event listeners after <Editable />'s focus handlers in React >= 17

Issue
Fixes: #4754

Example
See #4754 / #4755 - happy to provide more evidence if necessary

Context
In #4754, @jhurwitz noted that "the useFocused hook displays old values". I noticed this issue in my project as well, and the fix in #4755 solved the problem for me. However as noted by @bryanph, this change caused a regression when selecting between two different editors. Thus #4755 was reverted in #4874.

By chance, I noticed that running @jhurwitz's code sandbox in slate's examples does not cause the "inverted" focus behaviour, and after some digging I realized that the difference is that the code sandbox uses React 17 whereas slate's examples use React 16. To validate this, I cloned the sandbox and downgraded to React 16.

After some debugging, I noticed that the order of execution of <Editable />'s onFocus handler and <Slate />'s focus event listener were reversed in React 17. This led me to look at the React changelog, and it became clear that all this comes down to a change in how onFocus and onBlur are implemented in React 17 (see facebook/react#19186). As I understand it, onFocus and onBlur now listen to the focusin and focusout events during the bubbling phase.

Based on the above, I reasoned that listening to the focusin and focusout events without useCapture in <Slate /> should fix the order of execution. I'm happy to report that this is indeed the case!

For backwards compatibility, I added a React version check, and only use the focusin and focusout events if React >= 17. However, I've since tested using the focusin and focusout events no matter the React version and it doesn't seem to cause any issues (the focus behaviour in React 16 and 17 look identical and all tests still passing). Let me know whether it would be preferable to remove the version check.

Lastly, it would be good to get some input from @bryanph to verify that this doesn't cause the same issues as #4755 for them.

Checks

  • The new code matches the existing patterns and styles.
  • The tests pass with yarn test.
  • The linter passes with yarn lint. (Fix errors with yarn fix.)
  • The relevant examples still work. (Run examples with yarn start.)
  • You've added a changeset if changing functionality. (Add one with yarn changeset add.)

See facebook/react#19186 for details on changes to `onFocus` and `onBlur`
@changeset-bot
Copy link

changeset-bot bot commented Mar 31, 2022

🦋 Changeset detected

Latest commit: 96538b6

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
slate-react Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@adri1wald adri1wald changed the title run focus event listener after existing onFocus handlers (react >= 17) Run <Slate />'s focus event listeners after <Editable />'s focus handlers in React >= 17 Mar 31, 2022
Copy link
Collaborator

@dylans dylans left a comment

Choose a reason for hiding this comment

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

Please add a changeset and then I'll land this. Thanks @adri1wald!

@adri1wald adri1wald requested a review from dylans March 31, 2022 13:21
@bryanph
Copy link
Contributor

bryanph commented Apr 1, 2022

Don't really have the time to test this thoroughly at the moment, but the reasoning makes sense to me. Nice catch @adri1wald and thanks for the detailed explanation!

return () => {
document.removeEventListener('focus', fn, true)
document.removeEventListener('blur', fn, true)
if (IS_REACT_VERSION_17_OR_ABOVE) {
Copy link
Contributor

@bryanph bryanph Apr 1, 2022

Choose a reason for hiding this comment

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

My only remark would be to add a little summary of your explanation in the PR here just to make it easier to figure out why this code is here in the future

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Have added a summary of my explanation!

@dylans dylans merged commit f6b7ca1 into ianstormtaylor:main Apr 3, 2022
@github-actions github-actions bot mentioned this pull request Apr 3, 2022
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.

useFocused hook displays old value
3 participants