mirror of
https://github.com/pmndrs/zustand.git
synced 2025-12-08 19:45:52 +00:00
Update nextjs guide (#2328)
* Update nextjs guide * Minor changes * Minor changes * Minor changes * Minor changes * Minor changes * Minor changes
This commit is contained in:
parent
eb8443faa3
commit
8c1a1f0327
@ -36,9 +36,41 @@ We have these general recommendations for the appropriate use of Zustand:
|
||||
Let's write our store factory function that will create a new store for each
|
||||
request.
|
||||
|
||||
```json
|
||||
// tsconfig.json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
```
|
||||
|
||||
> **Note:** do not forget to remove all comments from your `tsconfig.json` file.
|
||||
|
||||
```ts
|
||||
// stores/counter-store.ts
|
||||
import { createStore } from 'zustand'
|
||||
// src/stores/counter-store.ts
|
||||
import { createStore } from 'zustand/vanilla'
|
||||
|
||||
export type CounterState = {
|
||||
count: number
|
||||
@ -71,14 +103,17 @@ export const createCounterStore = (
|
||||
Let's use the `createCounterStore` in our component and share it using a context provider.
|
||||
|
||||
```tsx
|
||||
// components/counter-store-provider.tsx
|
||||
// src/providers/counter-store-provider.tsx
|
||||
'use client'
|
||||
|
||||
import { type ReactNode, createContext, useRef, useContext } from 'react'
|
||||
import { type StoreApi, useStore } from 'zustand'
|
||||
|
||||
import { type CounterStore, createCounterStore } from 'stores/counter-store.ts'
|
||||
import { type CounterStore, createCounterStore } from '@/stores/counter-store'
|
||||
|
||||
export const CounterStoreContext = createContext()
|
||||
export const CounterStoreContext = createContext<StoreApi<CounterStore> | null>(
|
||||
null,
|
||||
)
|
||||
|
||||
export interface CounterStoreProviderProps {
|
||||
children: ReactNode
|
||||
@ -87,7 +122,7 @@ export interface CounterStoreProviderProps {
|
||||
export const CounterStoreProvider = ({
|
||||
children,
|
||||
}: CounterStoreProviderProps) => {
|
||||
const storeRef = useRef<CounterStore>()
|
||||
const storeRef = useRef<StoreApi<CounterStore>>()
|
||||
if (!storeRef.current) {
|
||||
storeRef.current = createCounterStore()
|
||||
}
|
||||
@ -99,10 +134,12 @@ export const CounterStoreProvider = ({
|
||||
)
|
||||
}
|
||||
|
||||
export const useCounterStore = (selector: (store: CounterStore) => T): T => {
|
||||
export const useCounterStore = <T,>(
|
||||
selector: (store: CounterStore) => T,
|
||||
): T => {
|
||||
const counterStoreContext = useContext(CounterStoreContext)
|
||||
|
||||
if (counterStoreContext === undefined) {
|
||||
if (!counterStoreContext) {
|
||||
throw new Error(`useCounterStore must be use within CounterStoreProvider`)
|
||||
}
|
||||
|
||||
@ -119,30 +156,64 @@ export const useCounterStore = (selector: (store: CounterStore) => T): T => {
|
||||
### Initializing the store
|
||||
|
||||
```ts
|
||||
// stores/counter-store.ts
|
||||
// rest of code
|
||||
// src/stores/counter-store.ts
|
||||
import { createStore } from 'zustand/vanilla'
|
||||
|
||||
export type CounterState = {
|
||||
count: number
|
||||
}
|
||||
|
||||
export type CounterActions = {
|
||||
decrementCount: () => void
|
||||
incrementCount: () => void
|
||||
}
|
||||
|
||||
export type CounterStore = CounterState & CounterActions
|
||||
|
||||
export const initCounterStore = (): CounterState => {
|
||||
return { count: new Date().getFullYear() }
|
||||
}
|
||||
|
||||
export const defaultInitState: CounterState = {
|
||||
count: 0,
|
||||
}
|
||||
|
||||
export const createCounterStore = (
|
||||
initState: CounterState = defaultInitState,
|
||||
) => {
|
||||
return createStore<CounterStore>()((set) => ({
|
||||
...initState,
|
||||
decrementCount: () => set((state) => ({ count: state.count - 1 })),
|
||||
incrementCount: () => set((state) => ({ count: state.count + 1 })),
|
||||
}))
|
||||
}
|
||||
```
|
||||
|
||||
```tsx
|
||||
// components/counter-store-provider.tsx
|
||||
// rest of code
|
||||
// src/providers/counter-store-provider.tsx
|
||||
'use client'
|
||||
|
||||
import { type ReactNode, createContext, useRef, useContext } from 'react'
|
||||
import { type StoreApi, useStore } from 'zustand'
|
||||
|
||||
import {
|
||||
type CounterStore,
|
||||
createCounterStore,
|
||||
initCounterStore,
|
||||
} from 'stores/counter-store.ts'
|
||||
} from '@/stores/counter-store'
|
||||
|
||||
// rest of code
|
||||
export const CounterStoreContext = createContext<StoreApi<CounterStore> | null>(
|
||||
null,
|
||||
)
|
||||
|
||||
export interface CounterStoreProviderProps {
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
export const CounterStoreProvider = ({
|
||||
children,
|
||||
}: CounterStoreProviderProps) => {
|
||||
const storeRef = useRef<CounterStore>()
|
||||
const storeRef = useRef<StoreApi<CounterStore>>()
|
||||
if (!storeRef.current) {
|
||||
storeRef.current = createCounterStore(initCounterStore())
|
||||
}
|
||||
@ -154,7 +225,17 @@ export const CounterStoreProvider = ({
|
||||
)
|
||||
}
|
||||
|
||||
// rest of code
|
||||
export const useCounterStore = <T,>(
|
||||
selector: (store: CounterStore) => T,
|
||||
): T => {
|
||||
const counterStoreContext = useContext(CounterStoreContext)
|
||||
|
||||
if (!counterStoreContext) {
|
||||
throw new Error(`useCounterStore must be use within CounterStoreProvider`)
|
||||
}
|
||||
|
||||
return useStore(counterStoreContext, selector)
|
||||
}
|
||||
```
|
||||
|
||||
### Using the store with different architectures
|
||||
@ -167,11 +248,13 @@ both architectures should be the same with slight differences related to each ar
|
||||
#### Pages Router
|
||||
|
||||
```tsx
|
||||
// components/pages/home-page.tsx
|
||||
import { useCounterStore } from 'components/counter-store-provider.ts'
|
||||
// src/components/pages/home-page.tsx
|
||||
import { useCounterStore } from '@/providers/counter-store-provider.ts'
|
||||
|
||||
export const HomePage = () => {
|
||||
const { count, incrementCount, decrementCount } = useCounterStore()
|
||||
const { count, incrementCount, decrementCount } = useCounterStore(
|
||||
(state) => state,
|
||||
)
|
||||
|
||||
return (
|
||||
<div>
|
||||
@ -189,10 +272,10 @@ export const HomePage = () => {
|
||||
```
|
||||
|
||||
```tsx
|
||||
// _app.tsx
|
||||
// src/_app.tsx
|
||||
import type { AppProps } from 'next/app'
|
||||
|
||||
import { CounterStoreProvider } from 'components/counter-store-provider.tsx'
|
||||
import { CounterStoreProvider } from '@/providers/counter-store-provider.tsx'
|
||||
|
||||
export default function App({ Component, pageProps }: AppProps) {
|
||||
return (
|
||||
@ -203,14 +286,23 @@ export default function App({ Component, pageProps }: AppProps) {
|
||||
}
|
||||
```
|
||||
|
||||
```tsx
|
||||
// src/pages/index.tsx
|
||||
import { HomePage } from '@/components/pages/home-page.tsx'
|
||||
|
||||
export default function Home() {
|
||||
return <HomePage />
|
||||
}
|
||||
```
|
||||
|
||||
> **Note:** creating a store per route would require creating and sharing the store
|
||||
> at page (route) component level. Try not to use this if you do not need to create
|
||||
> a store per route.
|
||||
|
||||
```tsx
|
||||
// pages/index.tsx
|
||||
import { CounterStoreProvider } from 'components/counter-store-provider.tsx'
|
||||
import { HomePage } from 'components/pages/home-page.tsx'
|
||||
// src/pages/index.tsx
|
||||
import { CounterStoreProvider } from '@/providers/counter-store-provider.tsx'
|
||||
import { HomePage } from '@/components/pages/home-page.tsx'
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
@ -224,11 +316,15 @@ export default function Home() {
|
||||
#### App Router
|
||||
|
||||
```tsx
|
||||
// components/pages/home-page.tsx
|
||||
import { useCounterStore } from 'components/counter-store-provider.ts'
|
||||
// src/components/pages/home-page.tsx
|
||||
'use client'
|
||||
|
||||
import { useCounterStore } from '@/providers/counter-store-provider'
|
||||
|
||||
export const HomePage = () => {
|
||||
const { count, incrementCount, decrementCount } = useCounterStore()
|
||||
const { count, incrementCount, decrementCount } = useCounterStore(
|
||||
(state) => state,
|
||||
)
|
||||
|
||||
return (
|
||||
<div>
|
||||
@ -246,15 +342,41 @@ export const HomePage = () => {
|
||||
```
|
||||
|
||||
```tsx
|
||||
// app/layout.tsx
|
||||
import { type ReactNode } from 'react'
|
||||
// src/app/layout.tsx
|
||||
import type { Metadata } from 'next'
|
||||
import { Inter } from 'next/font/google'
|
||||
import './globals.css'
|
||||
|
||||
export interface RootLayoutProps {
|
||||
children: ReactNode
|
||||
import { CounterStoreProvider } from '@/providers/counter-store-provider'
|
||||
|
||||
const inter = Inter({ subsets: ['latin'] })
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Create Next App',
|
||||
description: 'Generated by create next app',
|
||||
}
|
||||
|
||||
export default function RootLayout({ children }: RootLayoutProps) {
|
||||
return <CounterStoreProvider>{children}</CounterStoreProvider>
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={inter.className}>
|
||||
<CounterStoreProvider>{children}</CounterStoreProvider>
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
```tsx
|
||||
// src/app/page.tsx
|
||||
import { HomePage } from '@/components/pages/home-page'
|
||||
|
||||
export default function Home() {
|
||||
return <HomePage />
|
||||
}
|
||||
```
|
||||
|
||||
@ -263,9 +385,9 @@ export default function RootLayout({ children }: RootLayoutProps) {
|
||||
> a store per route.
|
||||
|
||||
```tsx
|
||||
// app/index.tsx
|
||||
import { CounterStoreProvider } from 'components/counter-store-provider.tsx'
|
||||
import { HomePage } from 'components/pages/home-page.tsx'
|
||||
// src/app/page.tsx
|
||||
import { CounterStoreProvider } from '@/providers/counter-store-provider'
|
||||
import { HomePage } from '@/components/pages/home-page'
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user