mirror of
https://github.com/pmndrs/zustand.git
synced 2025-12-08 19:45:52 +00:00
docs: general docs improvements (#3102)
* feat: general docs improvements * Update docs/guides/testing.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
parent
bd58db0faf
commit
48985a4cc2
@ -288,7 +288,7 @@ export default defineConfig((configEnv) =>
|
||||
)
|
||||
```
|
||||
|
||||
### Testing components
|
||||
### Testing Components
|
||||
|
||||
In the next examples we are going to use `useCounterStore`
|
||||
|
||||
@ -425,9 +425,7 @@ describe('Counter', () => {
|
||||
|
||||
expect(await screen.findByText(/^1$/)).toBeInTheDocument()
|
||||
|
||||
await act(async () => {
|
||||
await user.click(await screen.findByRole('button', { name: /one up/i }))
|
||||
})
|
||||
await user.click(await screen.findByRole('button', { name: /one up/i }))
|
||||
|
||||
expect(await screen.findByText(/^2$/)).toBeInTheDocument()
|
||||
})
|
||||
@ -495,9 +493,7 @@ describe('CounterWithContext', () => {
|
||||
|
||||
expect(await screen.findByText(/^1$/)).toBeInTheDocument()
|
||||
|
||||
await act(async () => {
|
||||
await user.click(await screen.findByRole('button', { name: /one up/i }))
|
||||
})
|
||||
await user.click(await screen.findByRole('button', { name: /one up/i }))
|
||||
|
||||
expect(await screen.findByText(/^2$/)).toBeInTheDocument()
|
||||
})
|
||||
@ -516,6 +512,229 @@ const renderCounterWithContext = () => {
|
||||
- Jest Demo: https://stackblitz.com/edit/jest-zustand
|
||||
- Vitest Demo: https://stackblitz.com/edit/vitest-zustand
|
||||
|
||||
### Testing Stores
|
||||
|
||||
In the next examples we are going to use `useCounterStore`
|
||||
|
||||
> **Note**: all of these examples are written using TypeScript.
|
||||
|
||||
```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 })),
|
||||
})
|
||||
```
|
||||
|
||||
```ts
|
||||
// stores/use-counter-store.ts
|
||||
import { create } from 'zustand'
|
||||
|
||||
import {
|
||||
type CounterStore,
|
||||
counterStoreCreator,
|
||||
} from '../shared/counter-store-creator'
|
||||
|
||||
export const useCounterStore = create<CounterStore>()(counterStoreCreator)
|
||||
```
|
||||
|
||||
```tsx
|
||||
// contexts/use-counter-store-context.tsx
|
||||
import { type ReactNode, createContext, useContext, useRef } from 'react'
|
||||
import { createStore } from 'zustand'
|
||||
import { useStoreWithEqualityFn } from 'zustand/traditional'
|
||||
import { shallow } from 'zustand/shallow'
|
||||
|
||||
import {
|
||||
type CounterStore,
|
||||
counterStoreCreator,
|
||||
} from '../shared/counter-store-creator'
|
||||
|
||||
export const createCounterStore = () => {
|
||||
return createStore<CounterStore>(counterStoreCreator)
|
||||
}
|
||||
|
||||
export type CounterStoreApi = ReturnType<typeof createCounterStore>
|
||||
|
||||
export const CounterStoreContext = createContext<CounterStoreApi | undefined>(
|
||||
undefined,
|
||||
)
|
||||
|
||||
export interface CounterStoreProviderProps {
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
export const CounterStoreProvider = ({
|
||||
children,
|
||||
}: CounterStoreProviderProps) => {
|
||||
const counterStoreRef = useRef<CounterStoreApi>(null)
|
||||
if (!counterStoreRef.current) {
|
||||
counterStoreRef.current = createCounterStore()
|
||||
}
|
||||
|
||||
return (
|
||||
<CounterStoreContext.Provider value={counterStoreRef.current}>
|
||||
{children}
|
||||
</CounterStoreContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export type UseCounterStoreContextSelector<T> = (store: CounterStore) => T
|
||||
|
||||
export const useCounterStoreContext = <T,>(
|
||||
selector: UseCounterStoreContextSelector<T>,
|
||||
): T => {
|
||||
const counterStoreContext = useContext(CounterStoreContext)
|
||||
|
||||
if (counterStoreContext === undefined) {
|
||||
throw new Error(
|
||||
'useCounterStoreContext must be used within CounterStoreProvider',
|
||||
)
|
||||
}
|
||||
|
||||
return useStoreWithEqualityFn(counterStoreContext, selector, shallow)
|
||||
}
|
||||
```
|
||||
|
||||
```tsx
|
||||
// components/counter/counter.tsx
|
||||
import { useCounterStore } from '../../stores/use-counter-store'
|
||||
|
||||
export function Counter() {
|
||||
const { count, inc } = useCounterStore()
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>Counter Store</h2>
|
||||
<h4>{count}</h4>
|
||||
<button onClick={inc}>One Up</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
```ts
|
||||
// components/counter/index.ts
|
||||
export * from './counter'
|
||||
```
|
||||
|
||||
```tsx
|
||||
// components/counter/counter.test.tsx
|
||||
import { act, render, screen } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
|
||||
import { Counter, useCounterStore } from '../../../stores/use-counter-store.ts'
|
||||
|
||||
describe('Counter', () => {
|
||||
test('should render with initial state of 1', async () => {
|
||||
renderCounter()
|
||||
|
||||
expect(useCounterStore.getState().count).toBe(1)
|
||||
})
|
||||
|
||||
test('should increase count by clicking a button', async () => {
|
||||
const user = userEvent.setup()
|
||||
|
||||
renderCounter()
|
||||
|
||||
expect(useCounterStore.getState().count).toBe(1)
|
||||
|
||||
await user.click(await screen.findByRole('button', { name: /one up/i }))
|
||||
|
||||
expect(useCounterStore.getState().count).toBe(2)
|
||||
})
|
||||
})
|
||||
|
||||
const renderCounter = () => {
|
||||
return render(<Counter />)
|
||||
}
|
||||
```
|
||||
|
||||
```tsx
|
||||
// components/counter-with-context/counter-with-context.tsx
|
||||
import {
|
||||
CounterStoreProvider,
|
||||
useCounterStoreContext,
|
||||
} from '../../contexts/use-counter-store-context'
|
||||
|
||||
const Counter = () => {
|
||||
const { count, inc } = useCounterStoreContext((state) => state)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>Counter Store Context</h2>
|
||||
<h4>{count}</h4>
|
||||
<button onClick={inc}>One Up</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const CounterWithContext = () => {
|
||||
return (
|
||||
<CounterStoreProvider>
|
||||
<Counter />
|
||||
</CounterStoreProvider>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
```tsx
|
||||
// components/counter-with-context/index.ts
|
||||
export * from './counter-with-context'
|
||||
```
|
||||
|
||||
```tsx
|
||||
// components/counter-with-context/counter-with-context.test.tsx
|
||||
import { act, render, screen } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
|
||||
import { CounterStoreContext } from '../../../contexts/use-counter-store-context'
|
||||
import { counterStoreCreator } from '../../../shared/counter-store-creator'
|
||||
|
||||
describe('CounterWithContext', () => {
|
||||
test('should render with initial state of 1', async () => {
|
||||
const counterStore = counterStoreCreator()
|
||||
|
||||
renderCounterWithContext(counterStore)
|
||||
|
||||
expect(counterStore.getState().count).toBe(1)
|
||||
expect(
|
||||
await screen.findByRole('button', { name: /one up/i }),
|
||||
).toBeInTheDocument()
|
||||
})
|
||||
|
||||
test('should increase count by clicking a button', async () => {
|
||||
const user = userEvent.setup()
|
||||
const counterStore = counterStoreCreator()
|
||||
|
||||
renderCounterWithContext(counterStore)
|
||||
|
||||
expect(counterStore.getState().count).toBe(1)
|
||||
|
||||
await user.click(await screen.findByRole('button', { name: /one up/i }))
|
||||
|
||||
expect(counterStore.getState().count).toBe(2)
|
||||
})
|
||||
})
|
||||
|
||||
const renderCounterWithContext = (store) => {
|
||||
return render(<CounterWithContext />, {
|
||||
wrapper: ({ children }) => (
|
||||
<CounterStoreContext.Provider value={store}>
|
||||
{children}
|
||||
</CounterStoreContext.Provider>
|
||||
),
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- **React Testing Library**: [React Testing Library (RTL)](https://testing-library.com/docs/react-testing-library/intro)
|
||||
|
||||
51
docs/llms.txt
Normal file
51
docs/llms.txt
Normal file
@ -0,0 +1,51 @@
|
||||
# Zustand
|
||||
|
||||
Zustand is a fast, minimal state management library for React and JavaScript. It provides a simple API without context providers or boilerplate.
|
||||
|
||||
## Docs
|
||||
|
||||
- [Official Docs](https://zustand.docs.pmnd.rs): Complete reference for Zustand.
|
||||
- [Getting Started](https://zustand.docs.pmnd.rs/getting-started/introduction): Learn the basics and create your first store.
|
||||
|
||||
## Core API
|
||||
|
||||
- [createStore](https://zustand.docs.pmnd.rs/apis/create-store): Create a standalone store without React.
|
||||
- [shallow](https://zustand.docs.pmnd.rs/apis/shallow): Utility for shallow comparison of `objects`.
|
||||
|
||||
## Middlewares
|
||||
|
||||
- [combine](https://zustand.docs.pmnd.rs/middlewares/combine): Combine multiple slices into a single store.
|
||||
- [devtools](https://zustand.docs.pmnd.rs/middlewares/devtools): Debug state changes using Redux DevTools.
|
||||
- [immer](https://zustand.docs.pmnd.rs/middlewares/immer): Use `Immer` for immutable state updates.
|
||||
- [persist](https://zustand.docs.pmnd.rs/middlewares/persist): Persist state to `localStorage` or other storage engines.
|
||||
- [redux](https://zustand.docs.pmnd.rs/middlewares/redux): Use a reducer-style setup similar to `Redux`.
|
||||
- [subscribeWithSelector](https://zustand.docs.pmnd.rs/middlewares/subscribe-with-selector): Subscribe to specific slices with selector support.
|
||||
|
||||
## React Bindings
|
||||
|
||||
- [create](https://zustand.docs.pmnd.rs/apis/create): Bind a store to React for use with hooks.
|
||||
- [createWithEqualityFn](https://zustand.docs.pmnd.rs/apis/create-with-equality-fn): Like `create`, but allows custom equality functions.
|
||||
|
||||
## React Hooks
|
||||
|
||||
- [useStore](https://zustand.docs.pmnd.rs/hooks/use-store): Access and subscribe to store state.
|
||||
- [useStoreWithEqualityFn](https://zustand.docs.pmnd.rs/hooks/use-store-with-equality-fn): Same as `useStore`, but with custom equality checks.
|
||||
- [useShallow](https://zustand.docs.pmnd.rs/hooks/use-shallow): Use selectors with shallow equality comparison.
|
||||
|
||||
## Migration Guides
|
||||
|
||||
- [Migrate to v4](https://zustand.docs.pmnd.rs/migrations/migrating-to-v4): How to upgrade from Zustand v3.
|
||||
- [Migrate to v5](https://zustand.docs.pmnd.rs/migrations/migrating-to-v5): How to upgrade from Zustand v4.
|
||||
|
||||
## Tutorials
|
||||
|
||||
- [Tic-Tac-Toe Guide](https://zustand.docs.pmnd.rs/guides/tutorial-tic-tac-toe): Build a game using Zustand state.
|
||||
|
||||
## More
|
||||
|
||||
- [Third-party Libraries](https://zustand.docs.pmnd.rs/integrations/third-party-libraries): Integrations with third-party libraries.
|
||||
- [Zustand vs Other Libraries](https://zustand.docs.pmnd.rs/getting-started/comparison): Comparison with other state management libraries.
|
||||
- [TypeScript](https://zustand.docs.pmnd.rs/guides/typescript): Guide to using Zustand with TypeScript.
|
||||
- [Testing](https://zustand.docs.pmnd.rs/guides/testing): Best practices for testing Zustand stores.
|
||||
- [Next.js](https://zustand.docs.pmnd.rs/guides/nextjs): Using Zustand with Next.js for state management.
|
||||
- [Slices Pattern](https://zustand.docs.pmnd.rs/guides/slices-pattern): Organizing state into slices for scalability and maintainability.
|
||||
@ -9,6 +9,10 @@ nav: 205
|
||||
`devtools` middleware lets you use [Redux DevTools Extension](https://github.com/reduxjs/redux-devtools)
|
||||
without Redux. Read more about the benefits of using [Redux DevTools for debugging](https://redux.js.org/style-guide/#use-the-redux-devtools-extension-for-debugging).
|
||||
|
||||
> [!IMPORTANT]
|
||||
> In order to use `devtools` from `zustand/middleware` you need to install
|
||||
> `@redux-devtools/extension` library.
|
||||
|
||||
```js
|
||||
const nextStateCreatorFn = devtools(stateCreatorFn, devtoolsOptions)
|
||||
```
|
||||
|
||||
@ -8,6 +8,10 @@ nav: 206
|
||||
|
||||
`immer` middleware lets you perform immutable updates.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> In order to use `immer` from `zustand/middleware/immer` you need to install
|
||||
> `immer` library.
|
||||
|
||||
```js
|
||||
const nextStateCreatorFn = immer(stateCreatorFn)
|
||||
```
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user