Skip to content

Commit 781fa77

Browse files
N-Ziermannleerob
andauthored
examples: improve zustand example (vercel#58696)
Co-authored-by: Lee Robinson <me@leerob.io>
1 parent 779a373 commit 781fa77

9 files changed

Lines changed: 81 additions & 72 deletions

File tree

examples/with-zustand/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
This example shows how to integrate Zustand in Next.js.
44

5-
This is an example on how you can use Zustand that also works with Next.js.
5+
This is an example on how you can use Zustand with Next.js.
66

77
All client components have access to the Zustand store using `useStore()` returned `store.ts` file.
88

examples/with-zustand/src/app/layout.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import StoreProvider from '@/lib/StoreProvider'
1+
import StoreProvider from '@/lib/storeProvider'
22

33
export default function RootLayout({
44
children,
@@ -8,7 +8,9 @@ export default function RootLayout({
88
return (
99
<html>
1010
<body>
11-
<StoreProvider>{children}</StoreProvider>
11+
<StoreProvider lastUpdate={new Date().getTime()}>
12+
{children}
13+
</StoreProvider>
1214
</body>
1315
</html>
1416
)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
.clock {
2+
padding: 15px;
3+
display: inline-block;
4+
color: #82fa58;
5+
font: 50px menlo, monaco, monospace;
6+
background-color: #000;
7+
border-radius: 4px;
8+
}
9+
10+
.light {
11+
background-color: #333;
12+
}

examples/with-zustand/src/components/clock.tsx

Lines changed: 17 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,44 +2,35 @@
22

33
import useInterval from '../lib/useInterval'
44
import { useStore } from '../lib/store'
5-
6-
const useClock = () => {
7-
return useStore((store) => ({
8-
lastUpdate: store.lastUpdate,
9-
light: store.light,
10-
}))
5+
import { useShallow } from 'zustand/react/shallow'
6+
import './clock.css'
7+
8+
function useClock() {
9+
return useStore(
10+
useShallow((store) => ({
11+
lastUpdate: store.lastUpdate,
12+
light: store.light,
13+
}))
14+
)
1115
}
1216

13-
const formatTime = (time: number) => {
14-
// cut off except hh:mm:ss
17+
function formatTime(time: number) {
18+
// hh:mm:ss
1519
return new Date(time).toJSON().slice(11, 19)
1620
}
1721

18-
const Clock = () => {
22+
function Clock() {
1923
const { lastUpdate, light } = useClock()
20-
24+
// alternative way to fetch single piece of state:
2125
const tick = useStore((store) => store.tick)
2226

23-
// Tick the time every second
2427
useInterval(() => {
25-
tick(Date.now(), true)
28+
tick(Date.now())
2629
}, 1000)
30+
2731
return (
28-
<div className={light ? 'light' : ''}>
32+
<div className={`clock ${light ? 'light' : ''}`}>
2933
{formatTime(lastUpdate)}
30-
<style jsx>{`
31-
div {
32-
padding: 15px;
33-
display: inline-block;
34-
color: #82fa58;
35-
font: 50px menlo, monaco, monospace;
36-
background-color: #000;
37-
}
38-
39-
.light {
40-
background-color: #999;
41-
}
42-
`}</style>
4334
</div>
4435
)
4536
}

examples/with-zustand/src/components/counter.tsx

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
'use client'
22

33
import { useStore } from '../lib/store'
4+
import { useShallow } from 'zustand/react/shallow'
45

5-
const useCounter = () => {
6-
return useStore((store) => ({
7-
count: store.count,
8-
increment: store.increment,
9-
decrement: store.decrement,
10-
reset: store.reset,
11-
}))
6+
function useCounter() {
7+
return useStore(
8+
useShallow((store) => ({
9+
count: store.count,
10+
increment: store.increment,
11+
decrement: store.decrement,
12+
reset: store.reset,
13+
}))
14+
)
1215
}
1316

14-
const Counter = () => {
17+
function Counter() {
1518
const { count, increment, decrement, reset } = useCounter()
1619
return (
1720
<div>

examples/with-zustand/src/lib/StoreProvider.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
'use client'
22

33
import { type PropsWithChildren, useRef } from 'react'
4-
import type { StoreType } from './store'
4+
import type { StoreInterface, StoreType } from './store'
55
import { initializeStore, Provider } from './store'
66

7-
const StoreProvider = ({ children, ...props }: PropsWithChildren) => {
7+
export interface PreloadedStoreInterface
8+
extends Pick<StoreInterface, 'lastUpdate'> {}
9+
10+
export default function StoreProvider({
11+
children,
12+
...props
13+
}: PropsWithChildren<PreloadedStoreInterface>) {
814
const storeRef = useRef<StoreType>()
915

1016
if (!storeRef.current) {
@@ -13,5 +19,3 @@ const StoreProvider = ({ children, ...props }: PropsWithChildren) => {
1319

1420
return <Provider value={storeRef.current}>{children}</Provider>
1521
}
16-
17-
export default StoreProvider
Lines changed: 24 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,59 @@
11
import { createContext, useContext } from 'react'
22
import { createStore, useStore as useZustandStore } from 'zustand'
3+
import { PreloadedStoreInterface } from './storeProvider'
34

4-
interface StoreInterface {
5+
export interface StoreInterface {
56
lastUpdate: number
67
light: boolean
78
count: number
8-
tick: (lastUpdate: number, light: boolean) => void
9+
tick: (lastUpdate: number) => void
910
increment: () => void
1011
decrement: () => void
1112
reset: () => void
1213
}
1314

14-
const getDefaultInitialState = () => ({
15-
lastUpdate: Date.now(),
16-
light: false,
17-
count: 0,
18-
})
15+
function getDefaultInitialState() {
16+
return {
17+
lastUpdate: new Date(1970, 1, 1).getTime(),
18+
light: false,
19+
count: 0,
20+
} as const
21+
}
1922

2023
export type StoreType = ReturnType<typeof initializeStore>
2124

22-
const zustandContext = createContext<StoreType | null>(null)
25+
const storeContext = createContext<StoreType | null>(null)
2326

24-
export const Provider = zustandContext.Provider
27+
export const Provider = storeContext.Provider
2528

26-
export const useStore = <T>(selector: (state: StoreInterface) => T) => {
27-
const store = useContext(zustandContext)
29+
export function useStore<T>(selector: (state: StoreInterface) => T) {
30+
const store = useContext(storeContext)
2831

2932
if (!store) throw new Error('Store is missing the provider')
3033

3134
return useZustandStore(store, selector)
3235
}
3336

34-
export const initializeStore = (
35-
preloadedState: Partial<StoreInterface> = {}
36-
) => {
37+
export function initializeStore(preloadedState: PreloadedStoreInterface) {
3738
return createStore<StoreInterface>((set, get) => ({
3839
...getDefaultInitialState(),
3940
...preloadedState,
40-
tick: (lastUpdate, light) => {
41+
tick: (lastUpdate) =>
4142
set({
4243
lastUpdate,
43-
light: !!light,
44-
})
45-
},
46-
increment: () => {
44+
light: !get().light,
45+
}),
46+
increment: () =>
4747
set({
4848
count: get().count + 1,
49-
})
50-
},
51-
decrement: () => {
49+
}),
50+
decrement: () =>
5251
set({
5352
count: get().count - 1,
54-
})
55-
},
56-
reset: () => {
53+
}),
54+
reset: () =>
5755
set({
5856
count: getDefaultInitialState().count,
59-
})
60-
},
57+
}),
6158
}))
6259
}

examples/with-zustand/src/lib/useInterval.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22
import { useEffect, useRef } from 'react'
33

44
// https://overreacted.io/making-setinterval-declarative-with-react-hooks/
5-
const useInterval = (callback: () => void, delay: number | undefined) => {
5+
export default function useInterval(
6+
callback: () => void,
7+
delay: number | undefined
8+
) {
69
const savedCallback = useRef<typeof callback>()
710

811
useEffect(() => {
@@ -18,5 +21,3 @@ const useInterval = (callback: () => void, delay: number | undefined) => {
1821
}
1922
}, [delay])
2023
}
21-
22-
export default useInterval

examples/with-zustand/tsconfig.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
{
22
"compilerOptions": {
3-
"target": "es5",
43
"lib": ["dom", "dom.iterable", "esnext"],
54
"allowJs": true,
65
"skipLibCheck": true,

0 commit comments

Comments
 (0)