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

support React 18 #122

Merged
merged 10 commits into from
May 25, 2022
Merged

support React 18 #122

merged 10 commits into from
May 25, 2022

Conversation

yen-tt
Copy link
Contributor

@yen-tt yen-tt commented May 23, 2022

This pr updates answers-headless-react to support React 18 as well as 17.

  • use alpha version 1.1.1.-alpha.0-95 of answers-headless which contains redux package update to support React 18.
  • to support react 18 concurrent mode, useSyncExternalStore is needed. This pr uses use-sync-external-store package for backwards-compatible with pre-18 versions (v17).
    • From internal docs in use-sync-external-store, the package does not offer much logic to support the case of server side rendering for react pre-18 versions. So, this pr update useAnswersState to be conditionally exported with either useAnswersStateWithManualStoreSync for SSR pre-18 or useAnswersStateWithReactStoreSync for everything else. updated to use use-sync-external-store as is, without the manual handling, for all cases (for more info).
    • with v17 version, there's warnings for state update on unmounted components, which we do get for when an async function (execute universal/vertical search) finish and send a series of dispatch that would trigger a re-render of a component that's already unmounted. There's no actual memory leak here since we always unsubscribe out listeners. This warning was removed in v18 (visit this PR for more details). As a workaround, isMountedRef is used in useAnswersStateWithReactStoreSync to track when a component is unmounted and prevent the callback for handlling store changes from triggering (cause component update).
    • update relevant packages to support v18 as well (@testing-library/react, @types/react)

Note: will release an alpha version of this for the component lib.

J=SLAP-2095
TEST=manual&auto

  • added new jest tests for server side rendering and hydration process.
  • see that jest tests passed successfully for V17 and V18. (note that @testing-library/react v13 drop support for 17 when beginning to support 18 in the same version)
  • test (client side) with React version 17 and React version 18 in test-site from component lib repo.
    • with React verison 18. test with render (pre-18 syntax) and createRoot (18), ran a couple search and switch between universal and vertical page. see that both works as expected, although there is a warning for the former approach (Warning: ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it's running React 17.) which is expected.
  • test (SSR) with React version 17 and React version 18 with yext-site-starters repo. See that the assets were built successfully and the page function as expected.

@yen-tt yen-tt requested a review from a team as a code owner May 23, 2022 17:05
@coveralls
Copy link

coveralls commented May 23, 2022

Coverage Status

Coverage decreased (-6.4%) to 63.333% when pulling b0459bb on dev/support-react-18 into dcd0f41 on develop.

package.json Outdated Show resolved Hide resolved
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const useAnswersState = (React as any).useSyncExternalStore === undefined && isServerEnvironment
? useAnswersStateWithManualStoreSync : useAnswersStateWithReactStoreSync;
Copy link
Contributor

Choose a reason for hiding this comment

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

so useAnswersStateWithManualStoreSync is only for react 17 SSR?

Copy link
Contributor

Choose a reason for hiding this comment

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

does useAnswersStateWithManualStoreSync work with react 18 as well?

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm also kind of curious if you read anything about what useSyncExternalStore is doing under the hood? If not then nbd

Copy link
Contributor Author

@yen-tt yen-tt May 24, 2022

Choose a reason for hiding this comment

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

When I was testing with startTransition to try to simulate tearing issues, useAnswersStateWithManualStoreSync doesn't work well with concurrent feature in react 18. Yup! useAnswersStateWithManualStoreSync would be for react 17 SSR only since use-sync-external-store library can handle the client side rendering for 17 and automatically use the builtin useSyncExternalStore API for 18.
I didn't dig too much into what useSyncExternalStore does internally but it looks like it is a reform of useMutableSource if you are familiar with that

src/useAnswersState.tsx Outdated Show resolved Hide resolved
@yen-tt yen-tt requested review from oshi97 and a team May 24, 2022 13:53
src/useAnswersState.tsx Outdated Show resolved Hide resolved
src/useAnswersState.tsx Outdated Show resolved Hide resolved
src/useAnswersState.tsx Outdated Show resolved Hide resolved
@oshi97
Copy link
Contributor

oshi97 commented May 24, 2022

Have we tried looking at what react-redux is doing? It looks like we match up pretty closely? https://github.com/reduxjs/react-redux/blob/master/src/hooks/useSelector.ts
It looks like they support react 16 and 17 though so I'm kind of curious if you have any idea how they're doing so? I haven't looked suuuper closely but it looks like they're also doing some kind of conditional export with src/index.ts and src/next.ts
https://github.com/reduxjs/react-redux/blob/master/package.json

@yen-tt
Copy link
Contributor Author

yen-tt commented May 24, 2022

Have we tried looking at what react-redux is doing? It looks like we match up pretty closely? https://github.com/reduxjs/react-redux/blob/master/src/hooks/useSelector.ts It looks like they support react 16 and 17 though so I'm kind of curious if you have any idea how they're doing so? I haven't looked suuuper closely but it looks like they're also doing some kind of conditional export with src/index.ts and src/next.ts https://github.com/reduxjs/react-redux/blob/master/package.json

After some digging into SSR, it look like component will NOT re-render so the logic to handle memoization of state selector and register subscription (useAnswersStateWithManualSyncStore) is not necessary - useEffect, which is used in useAnswersStateWithManualSyncStore, will not run in server. This make sense with the way that use-sync-external-store and react-redux also doesn't do much with this case of SSR pre-v18 other than directly returning the state snapshot. This would mean we need a way to apply initial state to redux BEFORE the call renderToString of the component in ssr, which would require some changes to AnswersHeadlessProvider and configureStore in answers-headless to accept a preloadedState option.

@yen-tt yen-tt requested a review from oshi97 May 24, 2022 21:40
package.json Outdated Show resolved Hide resolved
src/useAnswersState.tsx Outdated Show resolved Hide resolved
@@ -69,15 +101,15 @@ it('does not perform extra renders/listener registrations for nested components'
expect(childStateUpdates).toHaveLength(0);

userEvent.click(screen.getByText('Search'));
await pendingVerticalQuery;
await act( async () => { await pendingVerticalQuery; });
Copy link
Contributor

Choose a reason for hiding this comment

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

out of curiosity why are these acts necessary?

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 remember getting some warning about When testing, code that causes React state updates should be wrapped into act(...). I think we only mock the response of executeVerticalQuery and not the function itself, so it still would send dispatch calls that trigger state updates? I wasn't getting the right number of times state listeners and updates without act(..) wrapper. We do this for the other tests, just not in this one

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh ok, maybe this is due to changing from react 17 to 18 with how batching works or something. I think I wrote this original test in a pretty weird way but I couldn't think of an easy way to change it here so eh

@yen-tt yen-tt requested a review from oshi97 May 25, 2022 15:17
@yen-tt yen-tt merged commit 6848583 into develop May 25, 2022
@yen-tt yen-tt deleted the dev/support-react-18 branch May 25, 2022 18:20
@nmanu1 nmanu1 mentioned this pull request Jun 16, 2022
nmanu1 added a commit that referenced this pull request Jun 16, 2022
## Version 1.2.0
### Features
- Added support for React 16.14 and React 18 (#122, #124)
- Export Sandbox endpoints (#125)
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.

None yet

3 participants