mirror of
https://github.com/pmndrs/zustand.git
synced 2025-12-08 19:45:52 +00:00
Update guides (#2474)
* Update guides * Minor fixes * Minor changes * Update testing.md Co-authored-by: Blazej Sewera <code@sewera.dev> * Update testing.md Co-authored-by: Blazej Sewera <code@sewera.dev> --------- Co-authored-by: Blazej Sewera <code@sewera.dev>
This commit is contained in:
parent
e00c69f82b
commit
e8a5256548
@ -107,12 +107,14 @@ Let's use the `createCounterStore` in our component and share it using a context
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { type ReactNode, createContext, useRef, useContext } from 'react'
|
import { type ReactNode, createContext, useRef, useContext } from 'react'
|
||||||
import { type StoreApi, useStore } from 'zustand'
|
import { useStore } from 'zustand'
|
||||||
|
|
||||||
import { type CounterStore, createCounterStore } from '@/stores/counter-store'
|
import { type CounterStore, createCounterStore } from '@/stores/counter-store'
|
||||||
|
|
||||||
export const CounterStoreContext = createContext<StoreApi<CounterStore> | null>(
|
export type CounterStoreApi = ReturnType<typeof createCounterStore>
|
||||||
null,
|
|
||||||
|
export const CounterStoreContext = createContext<CounterStoreApi | undefined>(
|
||||||
|
undefined,
|
||||||
)
|
)
|
||||||
|
|
||||||
export interface CounterStoreProviderProps {
|
export interface CounterStoreProviderProps {
|
||||||
@ -122,7 +124,7 @@ export interface CounterStoreProviderProps {
|
|||||||
export const CounterStoreProvider = ({
|
export const CounterStoreProvider = ({
|
||||||
children,
|
children,
|
||||||
}: CounterStoreProviderProps) => {
|
}: CounterStoreProviderProps) => {
|
||||||
const storeRef = useRef<StoreApi<CounterStore>>()
|
const storeRef = useRef<CounterStoreApi>()
|
||||||
if (!storeRef.current) {
|
if (!storeRef.current) {
|
||||||
storeRef.current = createCounterStore()
|
storeRef.current = createCounterStore()
|
||||||
}
|
}
|
||||||
@ -194,7 +196,7 @@ export const createCounterStore = (
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { type ReactNode, createContext, useRef, useContext } from 'react'
|
import { type ReactNode, createContext, useRef, useContext } from 'react'
|
||||||
import { type StoreApi, useStore } from 'zustand'
|
import { useStore } from 'zustand'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
type CounterStore,
|
type CounterStore,
|
||||||
@ -202,8 +204,10 @@ import {
|
|||||||
initCounterStore,
|
initCounterStore,
|
||||||
} from '@/stores/counter-store'
|
} from '@/stores/counter-store'
|
||||||
|
|
||||||
export const CounterStoreContext = createContext<StoreApi<CounterStore> | null>(
|
export type CounterStoreApi = ReturnType<typeof createCounterStore>
|
||||||
null,
|
|
||||||
|
export const CounterStoreContext = createContext<CounterStoreApi | undefined>(
|
||||||
|
undefined,
|
||||||
)
|
)
|
||||||
|
|
||||||
export interface CounterStoreProviderProps {
|
export interface CounterStoreProviderProps {
|
||||||
@ -213,7 +217,7 @@ export interface CounterStoreProviderProps {
|
|||||||
export const CounterStoreProvider = ({
|
export const CounterStoreProvider = ({
|
||||||
children,
|
children,
|
||||||
}: CounterStoreProviderProps) => {
|
}: CounterStoreProviderProps) => {
|
||||||
const storeRef = useRef<StoreApi<CounterStore>>()
|
const storeRef = useRef<CounterStoreApi>()
|
||||||
if (!storeRef.current) {
|
if (!storeRef.current) {
|
||||||
storeRef.current = createCounterStore(initCounterStore())
|
storeRef.current = createCounterStore(initCounterStore())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -57,10 +57,25 @@ this means your application logic does not need to be changed or mocked when wri
|
|||||||
|
|
||||||
The mock provided below will enable the relevant test runner to reset the zustand stores after each test.
|
The mock provided below will enable the relevant test runner to reset the zustand stores after each test.
|
||||||
|
|
||||||
> **Warning:** In the following examples we are only supporting the curried version of create `(create()(...))`,
|
### Shared code just for testing purposes
|
||||||
> since it is supported on both JavaScript and TypeScript. If you are using the uncurried version of
|
|
||||||
> `create(...)` you will need to update your hooks to use the curried version, or update the mocks in order
|
This shared code was added to avoid code duplication in our demo since we use the same counter store
|
||||||
> to support the uncurried version.
|
creator for both implementations, with and without `Context` API — `createStore` and `create`, respectively.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// shared/counter-store-creator.ts
|
||||||
|
import { type StateCreator } from 'zustand'
|
||||||
|
|
||||||
|
export type CounterStore = {
|
||||||
|
count: number
|
||||||
|
inc: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export const counterStoreCreator: StateCreator<CounterStore> = (set) => ({
|
||||||
|
count: 1,
|
||||||
|
inc: () => set((state) => ({ count: state.count + 1 })),
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
### Jest
|
### Jest
|
||||||
|
|
||||||
@ -126,7 +141,7 @@ afterEach(() => {
|
|||||||
```
|
```
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
// src/setup-jest.ts
|
// setup-jest.ts
|
||||||
import '@testing-library/jest-dom'
|
import '@testing-library/jest-dom'
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -137,7 +152,7 @@ import type { JestConfigWithTsJest } from 'ts-jest'
|
|||||||
const config: JestConfigWithTsJest = {
|
const config: JestConfigWithTsJest = {
|
||||||
preset: 'ts-jest',
|
preset: 'ts-jest',
|
||||||
testEnvironment: 'jsdom',
|
testEnvironment: 'jsdom',
|
||||||
setupFilesAfterEnv: ['./src/setup-jest.ts'],
|
setupFilesAfterEnv: ['./setup-jest.ts'],
|
||||||
}
|
}
|
||||||
|
|
||||||
export default config
|
export default config
|
||||||
@ -218,7 +233,7 @@ afterEach(() => {
|
|||||||
> to add `import { afterEach, vi } from 'vitest'` at the top.
|
> to add `import { afterEach, vi } from 'vitest'` at the top.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
// __mocks__/vitest-env.d.ts
|
// global.d.ts
|
||||||
/// <reference types="vite/client" />
|
/// <reference types="vite/client" />
|
||||||
/// <reference types="vitest/globals" />
|
/// <reference types="vitest/globals" />
|
||||||
```
|
```
|
||||||
@ -227,10 +242,10 @@ afterEach(() => {
|
|||||||
> need to remove `/// <reference types="vitest/globals" />`.
|
> need to remove `/// <reference types="vitest/globals" />`.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
// src/setup-vitest.ts
|
// setup-vitest.ts
|
||||||
import '@testing-library/jest-dom'
|
import '@testing-library/jest-dom'
|
||||||
|
|
||||||
vi.mock('zustand') // to make it works like Jest (auto-mocking)
|
vi.mock('zustand') // to make it work like Jest (auto-mocking)
|
||||||
```
|
```
|
||||||
|
|
||||||
> **Note**: without [globals configuration](https://vitest.dev/config/#globals) enabled, we need
|
> **Note**: without [globals configuration](https://vitest.dev/config/#globals) enabled, we need
|
||||||
@ -278,39 +293,47 @@ export const counterStoreCreator: StateCreator<CounterStore> = (set) => ({
|
|||||||
// stores/user-counter-store.ts
|
// stores/user-counter-store.ts
|
||||||
import { create } from 'zustand'
|
import { create } from 'zustand'
|
||||||
|
|
||||||
import { type CounterStore, counterStoreCreator } from './counter-store-creator'
|
import {
|
||||||
|
type CounterStore,
|
||||||
|
counterStoreCreator,
|
||||||
|
} from '../shared/counter-store-creator'
|
||||||
|
|
||||||
export const useCounterStore = create<CounterStore>()(counterStoreCreator)
|
export const useCounterStore = create<CounterStore>()(counterStoreCreator)
|
||||||
```
|
```
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
// stores/use-counter-store-context.tsx
|
// contexts/use-counter-store-context.tsx
|
||||||
import {
|
import { type ReactNode, createContext, useContext, useRef } from 'react'
|
||||||
type PropsWithChildren,
|
import { createStore } from 'zustand'
|
||||||
createContext,
|
|
||||||
useContext,
|
|
||||||
useRef,
|
|
||||||
} from 'react'
|
|
||||||
import { type StoreApi, createStore } from 'zustand'
|
|
||||||
import { useStoreWithEqualityFn } from 'zustand/traditional'
|
import { useStoreWithEqualityFn } from 'zustand/traditional'
|
||||||
import { shallow } from 'zustand/shallow'
|
import { shallow } from 'zustand/shallow'
|
||||||
|
|
||||||
import { type CounterStore, counterStoreCreator } from './counter-store-creator'
|
import {
|
||||||
|
type CounterStore,
|
||||||
|
counterStoreCreator,
|
||||||
|
} from '../shared/counter-store-creator'
|
||||||
|
|
||||||
export const createCounterStore = () => {
|
export const createCounterStore = () => {
|
||||||
return createStore<CounterStore>(counterStoreCreator)
|
return createStore<CounterStore>(counterStoreCreator)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CounterStoreContext = createContext<StoreApi<CounterStore>>(
|
export type CounterStoreApi = ReturnType<typeof createCounterStore>
|
||||||
null as never,
|
|
||||||
|
export const CounterStoreContext = createContext<CounterStoreApi | undefined>(
|
||||||
|
undefined,
|
||||||
)
|
)
|
||||||
|
|
||||||
export type CounterStoreProviderProps = PropsWithChildren
|
export interface CounterStoreProviderProps {
|
||||||
|
children: ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
export const CounterStoreProvider = ({
|
export const CounterStoreProvider = ({
|
||||||
children,
|
children,
|
||||||
}: CounterStoreProviderProps) => {
|
}: CounterStoreProviderProps) => {
|
||||||
const counterStoreRef = useRef(createCounterStore())
|
const counterStoreRef = useRef<CounterStoreApi>()
|
||||||
|
if (!counterStoreRef.current) {
|
||||||
|
counterStoreRef.current = createCounterStore()
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CounterStoreContext.Provider value={counterStoreRef.current}>
|
<CounterStoreContext.Provider value={counterStoreRef.current}>
|
||||||
@ -400,7 +423,7 @@ const renderCounter = () => {
|
|||||||
import {
|
import {
|
||||||
CounterStoreProvider,
|
CounterStoreProvider,
|
||||||
useCounterStoreContext,
|
useCounterStoreContext,
|
||||||
} from '../../stores/use-counter-store-context'
|
} from '../../contexts/use-counter-store-context'
|
||||||
|
|
||||||
const Counter = () => {
|
const Counter = () => {
|
||||||
const { count, inc } = useCounterStoreContext((state) => state)
|
const { count, inc } = useCounterStoreContext((state) => state)
|
||||||
@ -470,8 +493,8 @@ const renderCounterWithContext = () => {
|
|||||||
|
|
||||||
**CodeSandbox Demos**
|
**CodeSandbox Demos**
|
||||||
|
|
||||||
- Jest Demo: https://codesandbox.io/p/sandbox/zustand-jest-demo-twkypn
|
- Jest Demo: https://stackblitz.com/edit/jest-zustand
|
||||||
- Vitest Demo: https://codesandbox.io/p/sandbox/zustand-vitest-demo-ph5gnj
|
- Vitest Demo: https://stackblitz.com/edit/vitest-zustand
|
||||||
|
|
||||||
## References
|
## References
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user