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 React 18 compatibility #1326

Merged
merged 17 commits into from
Apr 12, 2022
Merged

add React 18 compatibility #1326

merged 17 commits into from
Apr 12, 2022

Conversation

RobinMalfait
Copy link
Member

This PR will add React 18 compatibility. The biggest issue was related to our Transition
component so we re-designed it so that it works with React 18.

This PR will also prefer to use the useId hook provided by React itself if you are using React 18, otherwise we will fallback to our implementation.
Headless UI is still compatible with React 16 & 17, but our tests and playgrounds are using React 18 now.

Closes: #926
Fixes: #681

In React 16 & 17, where `useId` doesn't exist, we will fallback to our
implementation we have been using up until now.

The `useId` exposed by React 18, ensures stable references even in SSR
environments.
React 18 now uses the proper events:
- `blur` -> `focusout`
- `focus` -> `focusin`
This is a bit unfortunate, but since React 18 now does an extra
unmount/remount in `StrictMode` to ensure that your code is
ConcurrentMode ready, it takes a bit longer to settle what the DOM sees.

That said, this is a temporary "hack". We are going to experiment with
using tools like Puppeteer/Playwright to run our tests in an actual
browser instead to eliminate all the weird details that we have to keep
in mind.
Internally we would still use `el.focus()`, but this allows us to have
more control over that `focus` function.
- Improving the cleanup of useEffect hooks
- useIsoMorphicEffect instead of normal useEffect, so that we can use
  useLayoutEffect to be a bit quicker.
- This allows us to add event listeners on a node, and get automatic
  cleanup once `dispose` gets called.
- We also return all the `d.add` calls, so that we can cleanup specific
  parts only instead of everything or nothing.
@vercel
Copy link

vercel bot commented Apr 12, 2022

This pull request is being automatically deployed with Vercel (learn more).
To see the status of your deployments, click below or on the icon next to each commit.

headlessui-react – ./packages/playground-react

🔍 Inspect: https://vercel.com/tailwindlabs/headlessui-react/B5fYgRPYTtjb8xyK2uqgaKpLJSSc
✅ Preview: https://headlessui-react-git-react-18-compatibility-tailwindlabs.vercel.app

headlessui-vue – ./packages/playground-vue

🔍 Inspect: https://vercel.com/tailwindlabs/headlessui-vue/EVXwDLFyLuMinYvn8X7bQmVV5Ae1
✅ Preview: https://headlessui-vue-git-react-18-compatibility-tailwindlabs.vercel.app

@haf
Copy link

haf commented Apr 12, 2022

I wasn't able to test this code using https://yarnpkg.com/features/protocols/#git

Module not found: Can't resolve '@headlessui/react'
  1 | import { Fragment } from 'react'
> 2 | import { Popover, Transition } from '@headlessui/react'
  3 | import { RelativeDynamicTimeButton } from '.'
  4 | import classNames from 'classnames'
  5 | import DateRangeModalBody from './DateRangeModalBody'

When pointing to your package's workspace: "@headlessui/react": "tailwindlabs/headlessui#head=react-18-compatibility&workspace=@headlessui/react"

Most likely this is because we're missing a prepack script in the package.json file.

In the next transition we will remove _all_ classes provided and re-add
the once we need.
@RobinMalfait RobinMalfait merged commit fbfc86f into main Apr 12, 2022
@RobinMalfait RobinMalfait deleted the react-18-compatibility branch April 12, 2022 16:23
RobinMalfait added a commit that referenced this pull request Apr 13, 2022
* bump dev dependencies to React 18

* setup Jest to include `IS_REACT_ACT_ENVIRONMENT`

* prefer `useId` from React 18 if it exists

In React 16 & 17, where `useId` doesn't exist, we will fallback to our
implementation we have been using up until now.

The `useId` exposed by React 18, ensures stable references even in SSR
environments.

* update expected events

React 18 now uses the proper events:
- `blur` -> `focusout`
- `focus` -> `focusin`

* ensure to wait a bit longer

This is a bit unfortunate, but since React 18 now does an extra
unmount/remount in `StrictMode` to ensure that your code is
ConcurrentMode ready, it takes a bit longer to settle what the DOM sees.

That said, this is a temporary "hack". We are going to experiment with
using tools like Puppeteer/Playwright to run our tests in an actual
browser instead to eliminate all the weird details that we have to keep
in mind.

* prefer `.focus()` over `fireEvent.focus(el)`

* abstract `microTask` polyfill code

* prefer our `focus(el)` function over `el.focus()`

Internally we would still use `el.focus()`, but this allows us to have
more control over that `focus` function.

* add React 18 to the React Playground

* improve hooks for React 18

- Improving the cleanup of useEffect hooks
- useIsoMorphicEffect instead of normal useEffect, so that we can use
  useLayoutEffect to be a bit quicker.

* improve disposables

- This allows us to add event listeners on a node, and get automatic
  cleanup once `dispose` gets called.
- We also return all the `d.add` calls, so that we can cleanup specific
  parts only instead of everything or nothing.

* reimplement the Transition component to be React 18 ready

* wait an additional frame for everything to settle

* update playground examples

* suppressConsoleLogs for RadioGroup components

* update changelog

* keep the `to` classes for a smoother transition

In the next transition we will remove _all_ classes provided and re-add
the once we need.

---

Some extra special thanks:

- Thanks @silvenon for your initial work on the `transition` events in #926
- Thanks @thecrypticace for doing late-night debugging sessions

Co-authored-by: =?UTF-8?q?Matija=20Marohni=C4=87?= <matija.marohnic@gmail.com>
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
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.

React 18 compatibility tracking issue
2 participants