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

v5 #2138

Merged
merged 78 commits into from
Aug 16, 2024
Merged

v5 #2138

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
1528bd5
prepare for the next major version
dai-shi Oct 22, 2023
10466ab
Merge branch 'main' into v5
dai-shi Nov 1, 2023
aaa5c21
Merge branch 'main' into v5
dai-shi Nov 3, 2023
abbc740
Merge branch 'main' into v5
dai-shi Nov 14, 2023
39928ef
Merge branch 'main' into v5
dai-shi Nov 28, 2023
105bc57
Merge branch 'main' into v5
dai-shi Dec 9, 2023
8030aee
[v5] breaking: drop default exports (#2238)
charkour Dec 10, 2023
ee248eb
Merge branch 'main' into v5
dai-shi Dec 12, 2023
5c99468
Merge branch 'main' into v5
dai-shi Dec 13, 2023
6f99da1
[v5] breaking: drop deprecated features (#2235)
charkour Dec 14, 2023
3659165
[v5] breaking: make React 18 as minimal requirement (#2236)
charkour Dec 17, 2023
2059586
[v5] breaking: make use-sync-external-store an optional peer dependen…
charkour Dec 17, 2023
020f27b
Merge branch 'main' into v5
dai-shi Dec 17, 2023
4ffdb19
[v5] breaking: require TypeScript 4.5 and update tests (#2257)
dai-shi Dec 18, 2023
f910d1c
[v5]: drop "module" condition (#2270)
dbritto-dev Dec 22, 2023
85f1356
Merge branch 'main' into v5
dai-shi Dec 22, 2023
11250b1
Update package json to general exports and update node version (#2272)
dbritto-dev Dec 23, 2023
e75571f
[v5]: drop UMD/SystemJS builds (#2287)
dbritto-dev Jan 9, 2024
af5826b
Merge branch 'main' into v5
dai-shi Jan 10, 2024
be31b53
Merge branch 'main' into v5
dai-shi Jan 20, 2024
f7c0b50
remove `WithReact` type (#2300)
charkour Jan 20, 2024
25e731d
Merge branch 'main' into v5
dai-shi Jan 20, 2024
e5b64cc
5.0.0-alpha.0
dai-shi Jan 20, 2024
6c9944b
[v5]: do not depend on use-sync-external-store (#2301)
dai-shi Jan 20, 2024
43986a3
5.0.0-alpha.1
dai-shi Jan 20, 2024
f8a4d58
[v5]: refactor useMemoSelector (#2302)
dai-shi Jan 21, 2024
05f3578
[v5]: separate react entry point (#2303)
dai-shi Jan 21, 2024
9bf05b2
5.0.0-alpha.2
dai-shi Jan 21, 2024
7868b8c
Merge branch 'main' into v5
dai-shi Feb 17, 2024
bfea65a
5.0.0-alpha.3
dai-shi Feb 17, 2024
ffbf675
refactor: Switch to Object.hasOwn (#2365)
onlined Feb 29, 2024
77162b5
Merge branch 'main' into v5
dai-shi Mar 2, 2024
752b917
[v5] drop es5 (#2380)
dai-shi Mar 2, 2024
436156d
update yarn lock
dai-shi Mar 2, 2024
71891d8
5.0.0-alpha.4
dai-shi Mar 2, 2024
b19acdf
Merge branch 'main' into v5
dai-shi Mar 8, 2024
f87eec5
[v5]: follow React "standard" way with breaking behavioral change (#2…
dai-shi Mar 9, 2024
bfbbacc
5.0.0-alpha.5
dai-shi Mar 9, 2024
4ac0950
[v5] Rewrite shallow to support iterables (#2427)
dai-shi Mar 23, 2024
172ae1c
[v5] fix rollup config for cjs (#2433)
dai-shi Mar 23, 2024
73e2986
5.0.0-alpha.6
dai-shi Mar 23, 2024
ad2106a
Merge branch 'main' into v5
dai-shi Mar 23, 2024
4c4e4d1
Merge branch 'main' into v5
dai-shi Apr 6, 2024
68d84b3
no production build test
dai-shi Apr 6, 2024
8e6b914
recover types that are dropped in #2462
dai-shi Apr 6, 2024
a29fc6f
remove unused replacement
dai-shi Apr 7, 2024
36d4734
Merge branch 'main' into v5
dai-shi Apr 7, 2024
fe47d3e
[v5] Remove Devtools warning (#2466)
charkour Apr 16, 2024
263d63d
Merge branch 'main' into v5
dai-shi May 12, 2024
b2f4b50
update pnpm lock
dai-shi May 12, 2024
fca3b86
fix merge main
dai-shi May 12, 2024
4de1d1f
Merge branch 'main' into v5
dai-shi May 22, 2024
becc730
add migration guide
dai-shi May 22, 2024
75b16ed
fix typos
dai-shi May 22, 2024
e0502cf
5.0.0-beta.0
dai-shi May 22, 2024
c79d3fd
update migration doc
dai-shi May 23, 2024
31f04df
Merge branch 'main' into v5
dai-shi May 27, 2024
bb99cb5
Merge branch 'main' into v5
dai-shi Jun 4, 2024
2726e1e
Merge branch 'main' into v5
dai-shi Jun 5, 2024
a379a95
Merge branch 'main' into v5
dai-shi Jun 26, 2024
e4e0c7f
fix merge main
dai-shi Jun 26, 2024
a80061b
fix merge main (prettier)
dai-shi Jun 26, 2024
3842f19
5.0.0-beta.1
dai-shi Jun 26, 2024
5f0f34c
fix(types)!: require complete state if `setState`'s `replace` flag is…
Yonom Jun 29, 2024
2d05d5a
Merge branch 'main' into v5
dai-shi Jun 29, 2024
33e3fc4
5.0.0-beta.2
dai-shi Jun 29, 2024
cb952ac
move v5 migration doc
dai-shi Jun 29, 2024
8df22b9
Merge branch 'main' into v5
dai-shi Jul 9, 2024
88ef086
Merge branch 'main' into v5
dai-shi Jul 17, 2024
432cc22
Merge branch 'main' into v5
dai-shi Jul 20, 2024
3467a7a
fix ci
dai-shi Jul 20, 2024
1472402
Merge branch 'main' into v5
dai-shi Aug 13, 2024
5d2f20c
Merge branch 'main' into v5
dai-shi Aug 15, 2024
7930572
missing commmit
dai-shi Aug 15, 2024
9b86e6a
remove unused rule exclusion
dai-shi Aug 15, 2024
a46b619
comment about react compiler
dai-shi Aug 15, 2024
8e46d6a
revert eslint config
dai-shi Aug 15, 2024
d2f263f
Merge branch 'main' into v5
dai-shi Aug 16, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,7 @@
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-unused-vars": [
"warn",
{
"argsIgnorePattern": "^_",
"varsIgnorePattern": "^_",
"caughtErrorsIgnorePattern": "^_"
}
{ "argsIgnorePattern": "^_", "varsIgnorePattern": "^_" }
],
"@typescript-eslint/no-use-before-define": "off",
"@typescript-eslint/no-empty-function": "off",
Expand Down
21 changes: 2 additions & 19 deletions .github/workflows/test-multiple-builds.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ jobs:
strategy:
fail-fast: false
matrix:
build: [cjs, esm, umd]
env: [development, production]
build: [cjs, esm]
env: [development] # [development, production]
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
Expand All @@ -24,10 +24,6 @@ jobs:
cache-dependency-path: '**/pnpm-lock.yaml'
- run: pnpm install --frozen-lockfile
- run: pnpm build
- name: Use React 17 for production test
if: ${{ matrix.env == 'production' }}
run: |
pnpm add -D react@17.0.2 react-dom@17.0.2 @testing-library/react@12.1.4
- name: Patch for DEV-ONLY
if: ${{ matrix.env == 'development' }}
run: |
Expand All @@ -42,26 +38,13 @@ jobs:
if: ${{ matrix.build == 'cjs' }}
run: |
sed -i~ "s/resolve('\.\/src\(.*\)\.ts')/resolve('\.\/dist\1.js')/" vitest.config.ts
sed -i~ "s/module.exports.createStore = vanilla.createStore;//" dist/index.js
- name: Patch for ESM
if: ${{ matrix.build == 'esm' }}
run: |
sed -i~ "s/resolve('\.\/src\(.*\)\.ts')/resolve('\.\/dist\/esm\1.mjs')/" vitest.config.ts
sed -i~ "1s/^/import.meta.env=import.meta.env||{};import.meta.env.MODE='${NODE_ENV}';/" tests/*.tsx
env:
NODE_ENV: ${{ matrix.env }}
- name: Patch for UMD
if: ${{ matrix.build == 'umd' }}
run: |
sed -i~ "s/resolve('\.\/src\(.*\)\.ts')/resolve('\.\/dist\/umd\1.${NODE_ENV}.js')/" vitest.config.ts
env:
NODE_ENV: ${{ matrix.env }}
- name: Patch for SystemJS
if: ${{ matrix.build == 'system' }}
run: |
sed -i~ "s/resolve('\.\/src\(.*\)\.ts')/resolve('\.\/dist\/system\1.${NODE_ENV}.js')/" vitest.config.ts
env:
NODE_ENV: ${{ matrix.env }}
- name: Test ${{ matrix.build }} ${{ matrix.env }}
run: |
pnpm test:spec
Expand Down
30 changes: 0 additions & 30 deletions .github/workflows/test-multiple-versions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,31 +27,13 @@ jobs:
fail-fast: false
matrix:
react:
- 16.8.0
- 17.0.0
- 18.0.0
- 18.1.0
- 18.2.0
- 18.3.1
- 19.0.0-rc.0
- 19.0.0-rc-49496d49-20240814
- 0.0.0-experimental-49496d49-20240814
devtools-skip:
- CI-MATRIX-NOSKIP
include:
- devtools-skip: CI-MATRIX-[2345]
react: 16.8.0
- devtools-skip: CI-MATRIX-[1345]
react: 16.8.0
- devtools-skip: CI-MATRIX-[1245]
react: 16.8.0
- devtools-skip: CI-MATRIX-[1235]
react: 16.8.0
- devtools-skip: CI-MATRIX-[1234]
react: 16.8.0
exclude:
- devtools-skip: CI-MATRIX-NOSKIP
react: 16.8.0
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
Expand All @@ -61,18 +43,6 @@ jobs:
cache: 'pnpm'
cache-dependency-path: '**/pnpm-lock.yaml'
- run: pnpm install --frozen-lockfile
- name: Install legacy testing-library
if: ${{ startsWith(matrix.react, '16.') || startsWith(matrix.react, '17.') }}
run: pnpm add -D @testing-library/react@12.1.4
- name: Patch for React 16
if: ${{ startsWith(matrix.react, '16.') }}
run: |
sed -i~ '1s/^/import React from "react";/' tests/*.tsx
sed -i~ 's/"jsx": "react-jsx"/"jsx": "react"/' tsconfig.json
sed -i~ 's/import\.meta\.env[?]\.MODE/"DEVELOPMENT".toLowerCase()/' src/*.ts src/*/*.ts
sed -i~ "s/it('\[${DEVTOOLS_SKIP}\]/it.skip('/" tests/devtools.test.tsx
env:
DEVTOOLS_SKIP: ${{ matrix.devtools-skip }}
- name: Test ${{ matrix.react }} ${{ matrix.devtools-skip }}
run: |
pnpm add -D react@${{ matrix.react }} react-dom@${{ matrix.react }}
Expand Down
11 changes: 0 additions & 11 deletions .github/workflows/test-old-typescript.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,6 @@ jobs:
- 4.7.4
- 4.6.4
- 4.5.5
- 4.4.4
- 4.3.5
- 4.2.3
- 4.1.5
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
Expand All @@ -43,9 +39,6 @@ jobs:
sed -i~ 's/"verbatimModuleSyntax": true,//' tsconfig.json
- name: Patch for Old TS
run: |
sed -i~ 's/\/\/ @ts-expect-error.*\[LATEST-TS-ONLY\]//' tests/*.tsx
sed -i~ 's/"target":/"skipLibCheck":true,"target":/' tsconfig.json
sed -i~ 's/"exactOptionalPropertyTypes": true,//' tsconfig.json
sed -i~ 's/"moduleResolution": "bundler",/"moduleResolution": "node",/' tsconfig.json
sed -i~ 's/"allowImportingTsExtensions": true,//' tsconfig.json
sed -i~ 's/"zustand": \["\.\/src\/index\.ts"\],/"zustand": [".\/dist\/index.d.ts"],/' tsconfig.json
Expand All @@ -55,9 +48,5 @@ jobs:
pnpm add -D @types/node@18.13.0
- name: Install old TypeScript
run: pnpm add -D typescript@${{ matrix.typescript }}
- name: Patch testing setup for Old TS
if: ${{ matrix.typescript == '4.4.4' || matrix.typescript == '4.3.5' || matrix.typescript == '4.2.3' || matrix.typescript == '4.1.5' }}
run: |
pnpm add -D vitest@0.33.0 @vitest/coverage-v8@0.33.0 @vitest/ui@0.33.0
- name: Test ${{ matrix.typescript }}
run: pnpm test:types
28 changes: 0 additions & 28 deletions babel.config.js

This file was deleted.

30 changes: 30 additions & 0 deletions docs/guides/typescript.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,36 @@ For a usual statically typed language, this is impossible. But thanks to TypeScr

If you are eager to know what the answer is to this particular problem then you can [see it here](#middleware-that-changes-the-store-type).

### Handling Dynamic `replace` Flag

If the value of the `replace` flag is not known at compile time and is determined dynamically, you might face issues. To handle this, you can use a workaround by annotating the `replace` parameter with `as any`:

```ts
const replaceFlag = Math.random() > 0.5
store.setState(partialOrFull, replaceFlag as any)
```

#### Example with `as any` Workaround

```ts
import { create } from 'zustand'

interface BearState {
bears: number
increase: (by: number) => void
}

const useBearStore = create<BearState>()((set) => ({
bears: 0,
increase: (by) => set((state) => ({ bears: state.bears + by })),
}))

const replaceFlag = Math.random() > 0.5
useBearStore.setState({ bears: 5 }, replaceFlag as any) // Using the workaround
```

By following this approach, you can ensure that your code handles dynamic `replace` flags without encountering type issues.

## Common recipes

### Middleware that doesn't change the store type
Expand Down
171 changes: 171 additions & 0 deletions docs/migrations/migrating-to-v5.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
---
title: 'How to Migrate to v5 from v4'
nav: 30
---

# How to Migrate to v5 from v4

We highly recommend to update to the latest version of v4, before migrating to v5. It will show all deprecation warnings without breaking your app.

## Changes in v5

- Drop default exports
- Drop deprecated features
- Make React 18 the minimum required version
- Make use-sync-external-store a peer dependency (required for `createWithEqualityFn` and `useStoreWithEqualityFn` in `zustand/traditional`)
- Make TypeScript 4.5 the minimum required version
- Drop UMD/SystemJS support
- Organize entry points in the package.json
- Drop ES5 support
- Stricter types when setState's replace flag is set
- Other small improvements (technically breaking changes)

## Migration Guide

### Using custom equality functions such as `shallow`

The `create` function in v5 does not support customizing equality function.

If you use custom equality function such as `shallow`,
the easiest migration is to use `createWithEqualityFn`.

```js
// v4
import { create } from 'zustand'
import { shallow } from 'zustand/shallow'

const useCountStore = create((set) => ({
count: 0,
text: 'hello',
// ...
}))

const Component = () => {
const { count, text } = useCountStore(
(state) => ({
count: state.count,
text: state.text,
}),
shallow,
)
// ...
}
```

That can be done with `createWithEqualityFn` in v5:

```bash
npm install use-sync-external-store
```

```js
// v5
import { createWithEqualityFn as create } from 'zustand/traditional'

// The rest is the same as v4
```

Alternatively, for the `shallow` use case, you can use `useShallow` hook:

```js
// v5
import { create } from 'zustand'
import { useShallow } from 'zustand/shallow'

const useCountStore = create((set) => ({
count: 0,
text: 'hello',
// ...
}))

const Component = () => {
const { count, text } = useCountStore(
useShallow((state) => ({
count: state.count,
text: state.text,
})),
)
// ...
}
```

### Requiring stable selector outputs

There is a behavioral change in v5 to match React default behavior.
If a selector returns a new reference, it may cause infinite loops.

For example, this may cause infinite loops.

```js
// v4
const action = useMainStore((state) => {
return state.action ?? () => {}
})
```

The error message will be something like this:

```
Uncaught Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
```

To fix it, make sure the selector function returns a stable reference.

```js
// v5

const FALLBACK_ACTION = () => {}

const action = useMainStore((state) => {
return state.action ?? FALLBACK_ACTION
})
```

Alternatively, if you need v4 behavior, `createWithEqualityFn` will do.

```js
// v5
import { createWithEqualityFn as create } from 'zustand/traditional'
```

### Stricter types when setState's replace flag is set (Typescript only)

```diff
- setState:
- (partial: T | Partial<T> | ((state: T) => T | Partial<T>), replace?: boolean | undefined) => void;
+ setState:
+ (partial: T | Partial<T> | ((state: T) => T | Partial<T>), replace?: false) => void;
+ (state: T | ((state: T) => T), replace: true) => void;
```

If you are not using the `replace` flag, no migration is required.

If you are using the `replace` flag and it's set to `true`, you must provide a complete state object.
This change ensures that `store.setState({}, true)` (which results in an invalid state) is no longer considered valid.

**Examples:**

```ts
// Partial state update (valid)
store.setState({ key: 'value' })

// Complete state replacement (valid)
store.setState({ key: 'value' }, true)

// Incomplete state replacement (invalid)
store.setState({}, true) // Error
```

#### Handling Dynamic `replace` Flag

If the value of the `replace` flag is dynamic and determined at runtime, you might face issues. To handle this, you can use a workaround by annotating the `replace` parameter with `as any`:

```ts
const replaceFlag = Math.random() > 0.5
store.setState(partialOrFull, replaceFlag as any)
```

## Links

- https://github.com/pmndrs/zustand/pull/2138
- https://github.com/pmndrs/zustand/pull/2580
Loading