bb7b75809d
Made SSG and SSR code similar. Comments and variable renames on store.js Checklist: ## Documentation / Examples - [✓] Make sure the linting passes by running `yarn lint`
85 lines
2.6 KiB
JavaScript
85 lines
2.6 KiB
JavaScript
import { useLayoutEffect } from 'react'
|
|
import create from 'zustand'
|
|
import createContext from 'zustand/context'
|
|
|
|
let store
|
|
|
|
const getDefaultInitialState = () => ({
|
|
lastUpdate: Date.now(),
|
|
light: false,
|
|
count: 0,
|
|
})
|
|
|
|
const zustandContext = createContext()
|
|
|
|
export const Provider = zustandContext.Provider
|
|
// An example of how to get types
|
|
/** @type {import('zustand/index').UseStore<typeof initialState>} */
|
|
export const useStore = zustandContext.useStore
|
|
|
|
export const initializeStore = (preloadedState = {}) => {
|
|
return create((set, get) => ({
|
|
...getDefaultInitialState(),
|
|
...preloadedState,
|
|
tick: (lastUpdate, light) => {
|
|
set({
|
|
lastUpdate,
|
|
light: !!light,
|
|
})
|
|
},
|
|
increment: () => {
|
|
set({
|
|
count: get().count + 1,
|
|
})
|
|
},
|
|
decrement: () => {
|
|
set({
|
|
count: get().count - 1,
|
|
})
|
|
},
|
|
reset: () => {
|
|
set({
|
|
count: getDefaultInitialState().count,
|
|
})
|
|
},
|
|
}))
|
|
}
|
|
|
|
export function useCreateStore(serverInitialState) {
|
|
// Server side code: For SSR & SSG, always use a new store.
|
|
if (typeof window === 'undefined') {
|
|
return () => initializeStore(serverInitialState)
|
|
}
|
|
// End of server side code
|
|
|
|
// Client side code:
|
|
// Next.js always re-uses same store regardless of whether page is a SSR or SSG or CSR type.
|
|
const isReusingStore = Boolean(store)
|
|
store = store ?? initializeStore(serverInitialState)
|
|
// When next.js re-renders _app while re-using an older store, then replace current state with
|
|
// the new state (in the next render cycle).
|
|
// (Why next render cycle? Because react cannot re-render while a render is already in progress.
|
|
// i.e. we cannot do a setState() as that will initiate a re-render)
|
|
//
|
|
// eslint complaining "React Hooks must be called in the exact same order in every component render"
|
|
// is ignorable as this code runs in same order in a given environment (i.e. client or server)
|
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
useLayoutEffect(() => {
|
|
// serverInitialState is undefined for CSR pages. It is up to you if you want to reset
|
|
// states on CSR page navigation or not. I have chosen not to, but if you choose to,
|
|
// then add `serverInitialState = getDefaultInitialState()` here.
|
|
if (serverInitialState && isReusingStore) {
|
|
store.setState(
|
|
{
|
|
// re-use functions from existing store
|
|
...store.getState(),
|
|
// but reset all other properties.
|
|
...serverInitialState,
|
|
},
|
|
true // replace states, rather than shallow merging
|
|
)
|
|
}
|
|
})
|
|
|
|
return () => store
|
|
}
|