Revise v4 migrations doc page (#1507)

* docs(#1220): rephrase introduction to v4 migration

* docs(#1220): rephrase create function migration

* docs(#1220): split migration for create function paragraph

* docs(#1220): rename page to migrating to v4

* docs(#1220): restructure create function migration section

* docs(#1220): restructure StateCreator migration section

* docs(#1220): restructure PartialState migration section

* docs(#1220): restructure and rephrase useStore function migration

* docs(#1220): restructure all sections and reformat UseBoundStore and createContext

* docs(#1220): fix applicable import for createContext

* docs(#1220): rephrase persist middleware migration

* docs(#1220): rephrase redux middleware migration

* docs(#1220): simplify persist function migration
This commit is contained in:
Blazej Sewera 2023-01-07 02:50:02 +01:00 committed by GitHub
parent 0ef5d1e81b
commit 820dbd35ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 362 additions and 222 deletions

View File

@ -0,0 +1,362 @@
---
title: Migrating to v4
nav: 19
---
The only breaking changes are in types.
If you are using Zustand with TypeScript
or JSDoc type annotations,
this guide applies.
Otherwise, no migration is required.
Also, it's recommended to first read
the new [TypeScript Guide](../guides/typescript.md),
so that the migration is easier to understand.
In addition to this migration guide,
you can also check the
[diff](https://github.com/pmndrs/zustand/compare/v3.7.2...v4.0.0?short_path=37e5b4c#diff-c21e24854115b390eccde717da83f91feb2d5927a76c1485e5f0fdd0135c2afa)
of the test files in the Zustand repository from v3 to v4.
## `create`
**Applicable imports**
```ts
import create from 'zustand'
import create from 'zustand/vanilla'
```
**Change**
```diff
- create:
- < State
- , StoreSetState = StoreApi<State>["set"]
- , StoreGetState = StoreApi<State>["get"]
- , Store = StoreApi<State>
- >
- (f: ...) => ...
+ create:
+ { <State>(): (f: ...) => ...
+ , <State, Mutators>(f: ...) => ...
+ }
```
**Migration**
If you are not passing any type parameters to `create`,
no migration is required.
If you are using a "leaf" middleware like `combine` or `redux`,
remove all type parameters from `create`.
Else, replace `create<T, ...>(...)` with `create<T>()(...)`.
## `StateCreator`
**Applicable imports**
```ts
import type { StateCreator } from 'zustand'
import type { StateCreator } from 'zustand/vanilla'
```
**Change**
```diff
- type StateCreator
- < State
- , StoreSetState = StoreApi<State>["set"]
- , StoreGetState = StoreApi<State>["get"]
- , Store = StoreApi<State>
- > =
- ...
+ type StateCreator
+ < State
+ , InMutators extends [StoreMutatorIdentifier, unknown][] = []
+ , OutMutators extends [StoreMutatorIdentifier, unknown][] = []
+ , Return = State
+ > =
+ ...
```
**Migration**
If you are using `StateCreator`,
you are likely authoring a middleware
or using the "slices" pattern.
For that check the
[Authoring middlewares and advanced usage](../guides/typescript.md#authoring-middlewares-and-advanced-usage)
and [Common recipes](../guides/typescript.md#common-recipes)
sections of the TypeScript Guide.
## `PartialState`
**Applicable imports**
```ts
import type { PartialState } from 'zustand'
import type { PartialState } from 'zustand/vanilla'
```
**Change**
```diff
- type PartialState
- < T extends State
- , K1 extends keyof T = keyof T
- , K2 extends keyof T = K1
- , K3 extends keyof T = K2
- , K4 extends keyof T = K3
- > =
- | (Pick<T, K1> | Pick<T, K2> | Pick<T, K3> | Pick<T, K4> | T)
- | ((state: T) => Pick<T, K1> | Pick<T, K2> | Pick<T, K3> | Pick<T, K4> | T)
+ type PartialState<T> =
+ | Partial<T>
+ | ((state: T) => Partial<T>)
```
**Migration**
Replace `PartialState<T, ...>` with `PartialState<T>`
and preferably turn on [`exactOptionalPropertyTypes`](https://www.typescriptlang.org/tsconfig#exactOptionalPropertyTypes)
in your `tsconfig.json`:
```json
{
"compilerOptions": {
"exactOptionalPropertyTypes": true
}
}
```
We're no longer using the trick to disallow `{ foo: undefined }`
to be assigned to `Partial<{ foo: string }>`.
Instead, we're relying on the users to turn on `exactOptionalPropertyTypes`.
## `useStore`
**Applicable imports**
```ts
import { useStore } from 'zustand'
import { useStore } from 'zustand/react'
```
**Change**
```diff
- useStore:
- { <State>(store: StoreApi<State>): State
- , <State, StateSlice>
- ( store: StoreApi<State>
- , selector: StateSelector<State, StateSlice>,
- , equals?: EqualityChecker<StateSlice>
- ): StateSlice
- }
+ useStore:
+ <Store, StateSlice = ExtractState<Store>>
+ ( store: Store
+ , selector?: StateSelector<State, StateSlice>,
+ , equals?: EqualityChecker<StateSlice>
+ )
+ => StateSlice
```
**Migration**
If you are not passing any type parameters to `useStore`,
no migration is required.
If you are,
it's recommended to remove all the type parameters,
or pass the **store** type instead of the **state** type as the first parameter.
## `UseBoundStore`
**Applicable imports**
```ts
import type { UseBoundStore } from 'zustand'
import type { UseBoundStore } from 'zustand/react'
```
**Change**
```diff
- type UseBoundStore<
- State,
- Store = StoreApi<State>
- > =
- & { (): T
- , <StateSlice>
- ( selector: StateSelector<State, StateSlice>
- , equals?: EqualityChecker<StateSlice>
- ): U
- }
- & Store
+ type UseBoundStore<Store> =
+ & (<StateSlice = ExtractState<S>>
+ ( selector?: (state: ExtractState<S>) => StateSlice
+ , equals?: (a: StateSlice, b: StateSlice) => boolean
+ ) => StateSlice
+ )
+ & S
```
**Migration**
Replace `UseBoundStore<T>` with `UseBoundStore<StoreApi<T>>`,
and `UseBoundStore<T, S>` with `UseBoundStore<S>`
## `UseContextStore`
**Applicable imports**
```ts
import type { UseContextStore } from 'zustand/context'
```
**Change**
```diff
- type UseContextStore
```
**Migration**
Use `typeof MyContext.useStore` instead
## `createContext`
**Applicable imports**
```ts
import createContext from 'zustand/context'
```
**Change**
```diff
createContext:
- <State, Store = StoreApi<State>>() => ...
+ <Store>() => ...
```
**Migration**
Replace `createContext<T>()` with `createContext<StoreApi<T>>()`,
and `createContext<T, S>()` with `createContext<S>()`.
## `combine`, `devtools`, `subscribeWithSelector`
**Applicable imports**
```ts
import { combine } from 'zustand/middleware'
import { devtools } from 'zustand/middleware'
import { subscribeWithSelector } from 'zustand/middleware'
```
**Change**
```diff
- combine:
- <T, U>(...) => ...
+ combine:
+ <T, U, Mps, Mcs>(...) => ...
- devtools:
- <T>(...) => ...
+ devtools:
+ <T, Mps, Mcs>(...) => ...
- subscribeWithSelector:
- <T>(...) => ...
+ subscribeWithSelector:
+ <T, Mps, Mcs>(...) => ...
```
**Migration**
If you are not passing any type parameters
to `combine`, `devtools`, or `subscribeWithSelector`,
no migration is required.
If you are,
remove all the type parameters,
as they are inferred automatically.
## `persist`
**Applicable imports**
```ts
import { persist } from 'zustand/middleware'
```
**Change**
```diff
- persist:
- <T, U = Partial<T>>(...) => ...
+ persist:
+ <T, Mps, Mcs, U = T>(...) => ...
```
**Migration**
If you are passing any type parameters,
remove them as they are inferred automatically.
Next, if you are passing the `partialize` option,
there is no further steps required for migration.
If you are **not** passing the `partialize` option,
you might see some compilation errors.
If you do not see any,
there is no further migration required.
The type of partialized state is now `T` instead of `Partial<T>`,
which aligns with the runtime behavior of the default `partialize`,
which is an identity (`s => s`).
If you see some compilation errors,
you have to find and fix the errors yourself,
because they might be indicative of unsound code.
Alternatively, the workaround will be passing
`s => s as Partial<typeof s>` to `partialize`.
If your partialized state is truly `Partial<T>`,
you should not encounter any bugs.
The runtime behavior has not changed,
only the types are now correct.
## `redux`
**Applicable imports**
```ts
import { redux } from 'zustand/middleware'
```
**Change**
```diff
- redux:
- <T, A>(...) => ...
+ redux:
+ <T, A, Mps, Mcs>(...) => ...
```
**Migration**
If you are not passing any type parameters to `redux`,
no migration is required.
If you are,
remove all the type parameters,
and annotate only the second (action) parameter.
That is, replace `redux<T, A>((state, action) => ..., ...)`
with `redux((state, action: A) => ..., ...)`.

View File

@ -1,222 +0,0 @@
---
title: v4 Migrations
nav: 19
---
If you're not using the typed version (either via TypeScript or via JSDoc) then there are no breaking changes for you and hence no migration is needed either.
Also it's recommended to first read the new [TypeScript Guide](../guides/typescript.md), it'll be easier to understand the migration.
In addition to this migration guide you can also check the diff of the test files in the repo from v3 to v4.
## `create` (from `zustand` and `zustand/vanilla`)
### Change
```diff
- create:
- < State
- , StoreSetState = StoreApi<State>["set"]
- , StoreGetState = StoreApi<State>["get"]
- , Store = StoreApi<State>
- >
- (f: ...) => ...
+ create:
+ { <State>(): (f: ...) => ...
+ , <State, Mutators>(f: ...) => ...
+ }
```
### Migration
If you're not passing any type parameters to `create` then there is no migration needed. If you're using a "leaf" middleware like `combine` or `redux` then remove all type parameters from `create`. Else replace `create<T, ...>(...)` with `create<T>()(...)`.
## `StateCreator` (from `zustand` and `zustand/vanilla`)
### Change
```diff
- type StateCreator
- < State
- , StoreSetState = StoreApi<State>["set"]
- , StoreGetState = StoreApi<State>["get"]
- , Store = StoreApi<State>
- > =
- ...
+ type StateCreator
+ < State
+ , InMutators extends [StoreMutatorIdentifier, unknown][] = []
+ , OutMutators extends [StoreMutatorIdentifier, unknown][] = []
+ , Return = State
+ > =
+ ...
```
### Migration
If you're using `StateCreator` you're likely authoring a middleware or using the "slices" pattern, for that check the TypeScript Guide's ["Authoring middlewares and advanced usage"](../guides/typescript.md#authoring-middlewares-and-advanced-usage) and ["Common recipes"](../guides/typescript.md#common-recipes) sections.
## `PartialState` (from `zustand` and `zustand/vanilla`)
### Change
```diff
- type PartialState
- < T extends State
- , K1 extends keyof T = keyof T
- , K2 extends keyof T = K1
- , K3 extends keyof T = K2
- , K4 extends keyof T = K3
- > =
- | (Pick<T, K1> | Pick<T, K2> | Pick<T, K3> | Pick<T, K4> | T)
- | ((state: T) => Pick<T, K1> | Pick<T, K2> | Pick<T, K3> | Pick<T, K4> | T)
+ type PartialState<T> =
+ | Partial<T>
+ | ((state: T) => Partial<T>)
```
### Migration
Replace `PartialState<T, ...>` with `PartialState<T>` and preferably turn on [`--exactOptionalPropertyTypes`](https://www.typescriptlang.org/tsconfig#exactOptionalPropertyTypes).
We're no longer using the trick to disallow `{ foo: undefined }` to be assigned to `Partial<{ foo: string }>` instead now we're relying on the users to turn on `--exactOptionalPropertyTypes`.
## `useStore` (from `zustand` and `zustand/react`)
### Change
```diff
- useStore:
- { <State>(store: StoreApi<State>): State
- , <State, StateSlice>
- ( store: StoreApi<State>
- , selector: StateSelector<State, StateSlice>,
- , equals?: EqualityChecker<StateSlice>
- ): StateSlice
- }
+ useStore:
+ <Store, StateSlice = ExtractState<Store>>
+ ( store: Store
+ , selector?: StateSelector<State, StateSlice>,
+ , equals?: EqualityChecker<StateSlice>
+ )
+ => StateSlice
```
### Migration
If you're not passing any type parameters to `useStore` then there is no migration needed. If you are then it's recommended to remove them, or pass the store type instead of the state type as the first parameter.
## `UseBoundStore` (from `zustand` and `zustand/react`)
### Change
```diff
- type UseBoundStore<
- State,
- Store = StoreApi<State>
- > =
- & { (): T
- , <StateSlice>
- ( selector: StateSelector<State, StateSlice>
- , equals?: EqualityChecker<StateSlice>
- ): U
- }
- & Store
+ type UseBoundStore<Store> =
+ & (<StateSlice = ExtractState<S>>
+ ( selector?: (state: ExtractState<S>) => StateSlice
+ , equals?: (a: StateSlice, b: StateSlice) => boolean
+ ) => StateSlice
+ )
+ & S
```
### Migration
Replace `UseBoundStore<T>` with `UseBoundStore<StoreApi<T>>` and `UseBoundStore<T, S>` with `UseBoundStore<S>`
## `UseContextStore` (from `zustand/context`)
### Change
```diff
- type UseContextStore
```
### Migration
Use `typeof MyContext.useStore` instead
## `createContext` (from `zustand/context`)
### Change
```diff
createContext:
- <State, Store = StoreApi<State>>() => ...
+ <Store>() => ...
```
### Migration
Replace `createContext<T>()` with `createContext<StoreApi<T>>()` and `createContext<T, S>()` with `createContext<S>()`.
## `combine`, `devtools`, `subscribeWithSelector` (from `zustand/middleware`)
### Change
```diff
- combine:
- <T, U>(...) => ...
+ combine:
+ <T, U, Mps, Mcs>(...) => ...
- devtools:
- <T>(...) => ...
+ devtools:
+ <T, Mps, Mcs>(...) => ...
- subscribeWithSelector:
- <T>(...) => ...
+ subscribeWithSelector:
+ <T, Mps, Mcs>(...) => ...
```
### Migration
If you're not passing any type parameters then there is no migration needed. If you're passing any type parameters, remove them as are inferred.
## `persist` (from `zustand/middleware`)
### Change
```diff
- persist:
- <T, U = Partial<T>>(...) => ...
+ persist:
+ <T, Mps, Mcs, U = T>(...) => ...
```
### Migration
If you're passing any type parameters, then remove them because they will be inferred. Next, if you're passing the `partialize` option then there's no further steps required for migration.
But if you're not passing the `partialize` option then you might be seeing some compilation errors. If you're not seeing any compilation errors then there's no further steps requierd for migration.
But if you're seeing some compilation errors—because now the type of partialized state is `T` instead of `Partial<T>` which is in alignment with the runtime behavior of default `partialize` being `s => s`—then in that case you should fix the errors because they might be indicative of unsound code. To be clear the runtime behavior has not changed, the types have gotten more correct, but if your partialised state is truly `Partial<T>` then you can pass the `partialize` option as `s => s as Partial<typeof s>`. You can do this for a quickfix too.
## `redux` (from `zustand/middleware`)
### Change
```diff
- redux:
- <T, A>(...) => ...
+ redux:
+ <T, A, Mps, Mcs>(...) => ...
```
### Migration
If you're not passing any type parameters then there is no migration needed. If you're passing type parameters them remove them and annotate the second (action) parameter. That is replace `redux<T, A>((state, action) => ..., ...)` with `redux((state, action: A) => ..., ...)`.