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

test(ssr): Validate state synchronization between server and client in React 18 using Zustand (#903) #2088

Merged
merged 7 commits into from
Oct 2, 2023

Conversation

NaamuKim
Copy link
Contributor

@NaamuKim NaamuKim commented Sep 28, 2023

Related Issues or Discussions

Summary

This change tests state synchronization between server and client using React 18 and Zustand. It verifies that the server-rendered markup reflects the initial state correctly, and this state is accurately updated on the client side. This test is executed only when the version of React is 18.

Please suggest me if you have any other tests that might be helpful!

Check List

  • yarn run prettier for formatting code and docs

@vercel
Copy link

vercel bot commented Sep 28, 2023

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
zustand-demo ✅ Ready (Inspect) Visit Preview 💬 Add feedback Oct 2, 2023 0:59am

@codesandbox-ci
Copy link

codesandbox-ci bot commented Sep 28, 2023

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 24da89c:

Sandbox Source
React Configuration
React TypeScript Configuration
React Browserify Configuration
React Snowpack Configuration
React Parcel Configuration
Next.js Configuration
@pavlobu/zustand demo Configuration

@NaamuKim NaamuKim changed the title st(ssr): Validate state synchronization between server and client in React 18 using Zustand (#903) test(ssr): Validate state synchronization between server and client in React 18 using Zustand (#903) Sep 28, 2023
@dai-shi
Copy link
Member

dai-shi commented Sep 29, 2023

Can anyone review this PR please?

Copy link
Collaborator

@dbritto-dev dbritto-dev left a comment

Choose a reason for hiding this comment

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

Some ideas to make it work on react versions under 18

Comment on lines 1 to 8
import React, { useEffect } from 'react'
import { act, screen } from '@testing-library/react'
import { hydrateRoot } from 'react-dom/client'
import { renderToString } from 'react-dom/server'
import { describe, expect, it } from 'vitest'
import { create } from 'zustand'

const IS_REACT_18 = React.version.startsWith('18')
Copy link
Collaborator

Choose a reason for hiding this comment

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

We can skip any react version under 18

Suggested change
import React, { useEffect } from 'react'
import { act, screen } from '@testing-library/react'
import { hydrateRoot } from 'react-dom/client'
import { renderToString } from 'react-dom/server'
import { describe, expect, it } from 'vitest'
import { create } from 'zustand'
const IS_REACT_18 = React.version.startsWith('18')
import React, { useEffect } from 'react'
if (!React.version.startsWith('18')) {
describe.skip('skipping test on react version < 18', () => {})
return
}
import { act, screen } from '@testing-library/react'
import { hydrateRoot } from 'react-dom/client'
import { renderToString } from 'react-dom/server'
import { describe, expect, it } from 'vitest'
import { create } from 'zustand'

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, thank you for your review.
I understand your intention.
I resolved it by using the only method from vitest and dynamic import.
What do you think about it?

Copy link
Collaborator

@dbritto-dev dbritto-dev Oct 1, 2023

Choose a reason for hiding this comment

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

@NaamuKim that approach doesn't work you should completely avoid execute the next lines. Let's try with my suggestion.

Copy link
Contributor Author

@NaamuKim NaamuKim Oct 1, 2023

Choose a reason for hiding this comment

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

@dbritto-dev There's an issue with top-level returns not being allowed in ESM, causing errors.
I think your suggestion of using describe.skip is excellent, and I've configured it to conditionally branch describe based on the version.
Could you please confirm this?

Comment on lines 40 to 43
if (!IS_REACT_18) {
it('Dummy test for React 17, ignore', () => {})
return
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

We can omit this statement

Suggested change
if (!IS_REACT_18) {
it('Dummy test for React 17, ignore', () => {})
return
}

@dbritto-dev
Copy link
Collaborator

dbritto-dev commented Oct 1, 2023

@dai-shi would you mind triggering the tests again?

@dbritto-dev dbritto-dev self-requested a review October 1, 2023 15:09
Copy link
Collaborator

@dbritto-dev dbritto-dev left a comment

Choose a reason for hiding this comment

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

Some minor tweaks

Comment on lines 53 to 55
// Using dynamic import for 'react-dom/client' as it’s not accessible in React versions below 18.
// eslint-disable-next-line import/no-unresolved
const { hydrateRoot } = await require('react-dom/client')
Copy link
Collaborator

Choose a reason for hiding this comment

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

Move to the top and use vi.importActual instead of require

Suggested change
// Using dynamic import for 'react-dom/client' as it’s not accessible in React versions below 18.
// eslint-disable-next-line import/no-unresolved
const { hydrateRoot } = await require('react-dom/client')

const describeTest = React.version.startsWith('18') ? describe : describe.skip

describeTest('ssr behavior with react 18', () => {
interface BearStoreState {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
interface BearStoreState {
const { hydrateRoot } = await vi.importActual('react-dom/client')
interface BearStoreState {

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 want to use it like this, but if I change it to "skipIf" and it runs, it throws an error because it doesn't actually skip the context.

describe.skipIf(!React.version.startsWith('18'))(
  'ssr behavior with react 18',
  async () => {
    const {
      hydrateRoot,
    }: {
      hydrateRoot: (
        container: Element | Document,
        initialChildren: React.ReactNode
      ) => void
    } = await vi.importActual('react-dom/client')

throw error with a message Error: Failed to load url react-dom/client (resolved id: react-dom/client). Does the file exist?

If I move the other context inside the it context, it resolves the issue.

I've applied the changes with it.
Please take a look and feel free to share any further suggestions or improvements

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah I mean to the top of the context

import React, { useEffect } from 'react'
import { act, screen } from '@testing-library/react'
import { renderToString } from 'react-dom/server'
import { describe, expect, it } from 'vitest'
Copy link
Collaborator

Choose a reason for hiding this comment

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

Add vi on imports

Suggested change
import { describe, expect, it } from 'vitest'
import { describe, expect, it, vi } from 'vitest'

Comment on lines 7 to 9
const describeTest = React.version.startsWith('18') ? describe : describe.skip

describeTest('ssr behavior with react 18', () => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's use describe.skipIf

Suggested change
const describeTest = React.version.startsWith('18') ? describe : describe.skip
describeTest('ssr behavior with react 18', () => {
describe.skipIf(!React.version.startsWith('18'))('ssr behavior with react 18', () => {

Comment on lines 40 to 47
const {
hydrateRoot,
}: {
hydrateRoot: (
container: Element | Document,
initialChildren: React.ReactNode
) => void
} = await vi.importActual('react-dom/client')
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
const {
hydrateRoot,
}: {
hydrateRoot: (
container: Element | Document,
initialChildren: React.ReactNode
) => void
} = await vi.importActual('react-dom/client')
const { hydrateRoot } = await vi.importActual<typeof import('react-dom/client')>('react-dom/client')

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you for the suggestion. I've made the requested changes as you suggested.

Copy link
Member

@dai-shi dai-shi left a comment

Choose a reason for hiding this comment

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

Thanks for your contribution!

return <div>bears: {bears}</div>
}

describe.skipIf(!React.version.startsWith('18'))(
Copy link
Member

Choose a reason for hiding this comment

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

Oh, I didn't know about skipIf!

We should use it in jotai too:
https://github.com/pmndrs/jotai/blob/d6afb777705eb3a90385817fd6db30db101f6caa/tests/react/transition.test.tsx#L12C1-L16

Free free to open a PR there.

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 am also glad to have learned about skipIf, and I will apply refactoring accordingly in Jotai as well

Copy link
Collaborator

Choose a reason for hiding this comment

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

I guess we are good to merge the changes :D. BTW, good job.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you so much for your review.
I'm delighted to learn about the useful functions of vitest. I'll continue to check if there are any opportunities for further contributions.

@dai-shi dai-shi linked an issue Oct 2, 2023 that may be closed by this pull request
@dai-shi dai-shi merged commit 1e846b3 into pmndrs:main Oct 2, 2023
35 checks passed
bodinsamuel pushed a commit to specfy/specfy that referenced this pull request Nov 8, 2023
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [zustand](https://github.com/pmndrs/zustand) | [`4.4.1` ->
`4.4.6`](https://renovatebot.com/diffs/npm/zustand/4.4.1/4.4.6) |
[![age](https://developer.mend.io/api/mc/badges/age/npm/zustand/4.4.6?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/zustand/4.4.6?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/zustand/4.4.1/4.4.6?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/zustand/4.4.1/4.4.6?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>pmndrs/zustand (zustand)</summary>

### [`v4.4.6`](https://github.com/pmndrs/zustand/releases/tag/v4.4.6)

[Compare
Source](https://github.com/pmndrs/zustand/compare/v4.4.5...v4.4.6)

v4.4.5 has an issue with some TypeScript configs about module
resolution. It should be fixed now. Thanks for the patience.

#### What's Changed

- Update export types by
[@&#8203;dbritto-dev](https://github.com/dbritto-dev) in
[pmndrs/zustand#2170

#### New Contributors

- [@&#8203;cheatkey](https://github.com/cheatkey) made their first
contribution in
[pmndrs/zustand#2147
- [@&#8203;frixaco](https://github.com/frixaco) made their first
contribution in
[pmndrs/zustand#2166

**Full Changelog**:
pmndrs/zustand@v4.4.5...v4.4.6

### [`v4.4.5`](https://github.com/pmndrs/zustand/releases/tag/v4.4.5)

[Compare
Source](https://github.com/pmndrs/zustand/compare/v4.4.4...v4.4.5)

Hopefully, it should fix some issues with Node.js environment including
Next.js.

#### What's Changed

- fix: importing CJS React in ESM by
[@&#8203;dai-shi](https://github.com/dai-shi) in
[pmndrs/zustand#2154
- Apply publint recommendations by
[@&#8203;dbritto-dev](https://github.com/dbritto-dev) in
[pmndrs/zustand#2157

#### New Contributors

- [@&#8203;plrs9816](https://github.com/plrs9816) made their first
contribution in
[pmndrs/zustand#2137
- [@&#8203;Brammm](https://github.com/Brammm) made their first
contribution in
[pmndrs/zustand#2139
- [@&#8203;sobies93](https://github.com/sobies93) made their first
contribution in
[pmndrs/zustand#2142

**Full Changelog**:
pmndrs/zustand@v4.4.4...v4.4.5

### [`v4.4.4`](https://github.com/pmndrs/zustand/releases/tag/v4.4.4)

[Compare
Source](https://github.com/pmndrs/zustand/compare/v4.4.3...v4.4.4)

There was a tiny issue in v4.4.3, which broke with some bundlers, which
this version fixes.

#### What's Changed

- fix(build): patch entry points zustand/shallow for CJS by
[@&#8203;dai-shi](https://github.com/dai-shi) in
[pmndrs/zustand#2128

**Full Changelog**:
pmndrs/zustand@v4.4.3...v4.4.4

### [`v4.4.3`](https://github.com/pmndrs/zustand/releases/tag/v4.4.3)

[Compare
Source](https://github.com/pmndrs/zustand/compare/v4.4.2...v4.4.3)

The changes in v4.4.2 were troublesome for some users. This version
should fix/mitigate such cases.

#### What's Changed

- fix(shallow): Extract shallow vanilla and react by
[@&#8203;dbritto-dev](https://github.com/dbritto-dev) in
[pmndrs/zustand#2097
- fix(types): mitigate devtools typing by
[@&#8203;dai-shi](https://github.com/dai-shi) in
[pmndrs/zustand#2099

**Full Changelog**:
pmndrs/zustand@v4.4.2...v4.4.3

### [`v4.4.2`](https://github.com/pmndrs/zustand/releases/tag/v4.4.2)

[Compare
Source](https://github.com/pmndrs/zustand/compare/v4.4.1...v4.4.2)

This adds `useShallow` hook to cover some use cases that are deprecated
with v4.4.0 change. Check out [the
guide](https://github.com/pmndrs/zustand/blob/ec538e9d4c0b9b5759e6dfd0fd3c9a21f8236949/docs/guides/prevent-rerenders-with-use-shallow.md).

##### Migration Guide

[#&#8203;1991](https://github.com/pmndrs/zustand/issues/1991) requires
something like below if you are using the `devtools` middleware *and*
TypeScript.

```diff
  import { devtools } from 'zustand/middleware'
+ import type {} from '@&#8203;redux-devtools/extension'
```

##### What's Changed

- fix(types)(middleware/devtools): avoid copying types by
[@&#8203;dai-shi](https://github.com/dai-shi) in
[pmndrs/zustand#1991
- fix(traditional): make defaultEqualityFn optional in TS Types by
[@&#8203;charkour](https://github.com/charkour) in
[pmndrs/zustand#2060
- feat: add useShallow by
[@&#8203;FaberVitale](https://github.com/FaberVitale) in
[pmndrs/zustand#2090

##### New Contributors

- [@&#8203;aykutkardas](https://github.com/aykutkardas) made their
first contribution in
[pmndrs/zustand#1993
- [@&#8203;michelts](https://github.com/michelts) made their first
contribution in
[pmndrs/zustand#1997
- [@&#8203;elusive](https://github.com/elusive) made their first
contribution in
[pmndrs/zustand#2001
- [@&#8203;mayank1513](https://github.com/mayank1513) made their first
contribution in
[pmndrs/zustand#2015
- [@&#8203;fdb](https://github.com/fdb) made their first contribution
in
[pmndrs/zustand#2029
- [@&#8203;tmkx](https://github.com/tmkx) made their first
contribution in
[pmndrs/zustand#2032
- [@&#8203;OshriAsulin](https://github.com/OshriAsulin) made their
first contribution in
[pmndrs/zustand#2028
- [@&#8203;ivanquirino](https://github.com/ivanquirino) made their
first contribution in
[pmndrs/zustand#2047
- [@&#8203;stavkamil](https://github.com/stavkamil) made their first
contribution in
[pmndrs/zustand#2071
- [@&#8203;NaamuKim](https://github.com/NaamuKim) made their first
contribution in
[pmndrs/zustand#2088
- [@&#8203;FaberVitale](https://github.com/FaberVitale) made their
first contribution in
[pmndrs/zustand#2090

**Full Changelog**:
pmndrs/zustand@v4.4.1...v4.4.2

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "after 4pm every weekday" in timezone
Europe/Paris, Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Mend
Renovate](https://www.mend.io/free-developer-tools/renovate/). View
repository job log
[here](https://developer.mend.io/github/specfy/specfy).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy40Ni4wIiwidXBkYXRlZEluVmVyIjoiMzcuNDYuMCIsInRhcmdldEJyYW5jaCI6ImNob3JlL3Jlbm92YXRlQmFzZUJyYW5jaCJ9-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
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.

[test] add spec with ssr
3 participants