Update nextjs guide (#2328)

* Update nextjs guide

* Minor changes

* Minor changes

* Minor changes

* Minor changes

* Minor changes

* Minor changes
This commit is contained in:
Danilo Britto 2024-02-15 21:58:13 -05:00 committed by GitHub
parent eb8443faa3
commit 8c1a1f0327
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -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 (