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
Support React 18 #6743
Support React 18 #6743
Conversation
size-limit report 📦
|
13f713a
to
6dc27a8
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great @ryanwilsonperkin thank you for taking this on. I think next you'll want to /snapit
for testing in web. Let me know if you want a hand with that
} else { | ||
actionMarkup = primaryAction; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why was this needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
actionMarkup
will be used below and must be a ReactNode
, but the value that gets passed in from primaryAction
can be either a ReactNode
or an "interface" object with some properties on it. So what's happening here is that we're initially assigning it to T | ReactNode
and then in the isInterface
condition we're refining that to just ReactNode
, however for some reason the type assertion of the isInterface
utility isn't working correctly so lower down when we try to use this as a ReactNode
TypeScript still believes that it's either T | ReactNode
.
React itself used to be more permissive about this, but is stricter in v18 and flags this as a potential error. By telling the compiler that it can only ever be a ReactNode
and then assigning it as such in the two different conditions, we make the compiler happy, and don't change the functionality of the code.
polaris-react/src/components/Tabs/components/TabMeasurer/tests/TabMeasurer.test.tsx
Show resolved
Hide resolved
59ec524
to
03f6005
Compare
Rebased atop latest main. Only conflicts was some small ordering adjusts in package.json files |
React 18 changes up the timing of when certain effects will fire, we have cases where we use AfterInitialMount to mount something which contains a ref, but that ref won't be available to the parent component any longer during the same synchronous effect cycle. This change adds a callback prop to AfterInitialMount so that we can tell it to invoke the callback after it has rendered its children and we can use that to invoke the appropriate behaviour instead of doing it in a useEffect
Invoking resizeTableScrollBar prior to these refs being mounted is a no-op, we need to tell AfterInitialMount to invoke the function once its rendered the children so that we know the refs that we're operating on will be available.
Fixes broken tests by ensuring that the changes that are caused by these actions are updated and reflected in the component under test.
There's no need to reset modules here because the reference to NODE_ENV in the system under test is within the component and is not at the module level. It will be evaluated every time at runtime and can be safely mocked out without needing to reset the modules. This fix is needed because resetting the modules in this way results in the components under test being unmounted.
In React 18, state updates are no longer flushed synchronously to the component within event handlers. Rather than use state for this purpose, we should use a mutable ref so that we can easily increment the value and be sure that we're always referencing the latest value and not looking at a stale "position" number that hasn't yet received the update from the last event that was processed.
This test previously included the messaging that the setTimeoutSpy would be invoked twice because of a specific behaviour of react scheduler. This is no longer true under react 18 and we can use the regular assertion of "toHaveBeenCalled" instead of specifying that it happens twice.
Contains a fix to work properly with the version of @types/react that we're now using
In React 18 types, useCallback wrapped functions require their parameter types to be explicitly defined. Previously these would end up being implicit any. In one case here I've opted to remove the callback altogether because all it did was wrap a setState with the same value, and that setState function is already stable so it doesn't require the useCallback wrapper.
Previously content was assigned to a type "A | B" and then went into a conditional block that narrowed it to just "A". But the rendered content beneath still believed the type to be "A | B" which was allowed previously where React would treat it as potentially renderable, but it is now stricter and requires us to do more explicit type narrowing. We can do that by assigning the value conditionally in the two different cases so that it knows that it will _always_ be narrowed to "A"
We need to let wrapWithComponent know that in addition to the props that the component accepts, it might also receive the prop "key" prop which comes from JSX intrinsic attributes.
…nt count as ReactNode
By just using the `Parameter<typeof useCallback>[0]` previously this was being evaluated as simply "Function", so the value that was returned from useDeepCallback lost any of the particular typing of the function that it wrapped. By using this generic approach, the return value will receive the proper type signature.
Co-authored-by: Kyle Durand <kyledurand@users.noreply.github.com>
54bddb8
to
695d041
Compare
/snapit |
🫰✨ Thanks @chloerice! Your snapshots have been published to npm. Test the snapshots by updating your yarn add @shopify/plugin-polaris@0.0.0-snapshot-release-20221007181635 yarn add @shopify/polaris@0.0.0-snapshot-release-20221007181635 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for tackling this upgrade @ryanwilsonperkin!! This fixes some bugs affecting the admin, including the IndexTable scrollbar bug 🎉 (Spinstance)
@ryanwilsonperkin is vacationing (since Sept 27th, calendars suggest he'll be back on Tuesday). If the polaris crew is happy then I'm confident that this is good to merge without waiting for him to return and do the what-have-i-missed catchup. |
This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## @shopify/polaris@10.7.0 ### Minor Changes - [#6743](#6743) [`8d440aa6b`](8d440aa) Thanks [@ryanwilsonperkin](https://github.com/ryanwilsonperkin)! - Add support for React 18 - [#7359](#7359) [`6be4436d0`](6be4436) Thanks [@clarkjennings](https://github.com/clarkjennings)! - - Updated font size for square `Avatar` with 3-letter initials ## @shopify/plugin-polaris@0.0.9 ## polaris-for-figma@0.0.23 ### Patch Changes - [#6743](#6743) [`8d440aa6b`](8d440aa) Thanks [@ryanwilsonperkin](https://github.com/ryanwilsonperkin)! - Add support for React 18 - Updated dependencies \[[`8d440aa6b`](8d440aa), [`6be4436d0`](6be4436)]: - @shopify/polaris@10.7.0 ## polaris.shopify.com@0.21.1 ### Patch Changes - [#6743](#6743) [`8d440aa6b`](8d440aa) Thanks [@ryanwilsonperkin](https://github.com/ryanwilsonperkin)! - Add support for React 18 - [#7370](#7370) [`58cdc1671`](58cdc16) Thanks [@chloerice](https://github.com/chloerice)! - Removed 'About Polaris' page from the Foundations section, as the 'Getting started' section covers this - Updated dependencies \[[`8d440aa6b`](8d440aa), [`6be4436d0`](6be4436)]: - @shopify/polaris@10.7.0 Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
WHY are these changes introduced?
Fixes #5477
Adds explicit support for React 18 in the component library, website, and figma plugin. Fixes bugs and type errors that result.
WHAT is this pull request doing?
There are a few notable changes that we make in order to work with new behaviours in React 18:
AfterInitialMount
helper component gets a new proponMount
callback that will be invoked after the initial mount. This lets us avoid a timing issues in IndexTable that happens based on new mounting & effect processing behaviour that would otherwise cause a callback to invoked before a ref was actually available in the DOM.@shopify/react-testing
as well as proper use of theact
method throughout our tests.act
was previously more lenient but gets stricter in React 18 about exactly how and when it should be invoked.resetModules
in some of our tests which was interfering with a global behaviour of the@shopify/react-testing
libraryuseCallback
functions now require parameter signatures and functional components no longer implicitly receive achildren
prop, and need to declare it explicitly now.How to 🎩
🖥 Local development instructions
🗒 General tophatting guidelines
📄 Changelog guidelines
Copy-paste this code in
playground/Playground.tsx
:🎩 checklist
README.md
with documentation changes