Skip to content

Commit

Permalink
Merge branch 'canary' into tt-violation-fix-styles
Browse files Browse the repository at this point in the history
  • Loading branch information
ijjk committed May 22, 2022
2 parents 5f520b4 + bb7b758 commit b2dffd2
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 28 deletions.
51 changes: 33 additions & 18 deletions examples/with-zustand/lib/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,22 @@ import createContext from 'zustand/context'

let store

const initialState = {
lastUpdate: 0,
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) => ({
...initialState,
...getDefaultInitialState(),
...preloadedState,
tick: (lastUpdate, light) => {
set({
Expand All @@ -38,33 +39,47 @@ export const initializeStore = (preloadedState = {}) => {
},
reset: () => {
set({
count: initialState.count,
count: getDefaultInitialState().count,
})
},
}))
}

export function useCreateStore(initialState) {
// For SSR & SSG, always use a new store.
export function useCreateStore(serverInitialState) {
// Server side code: For SSR & SSG, always use a new store.
if (typeof window === 'undefined') {
return () => initializeStore(initialState)
return () => initializeStore(serverInitialState)
}
// End of server side code

// For CSR, always re-use same store.
store = store ?? initializeStore(initialState)
// And if initialState changes, then merge states in the next render cycle.
// 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
// 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(() => {
if (initialState && store) {
store.setState({
...store.getState(),
...initialState,
})
// 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
)
}
}, [initialState])
})

return () => store
}
2 changes: 1 addition & 1 deletion examples/with-zustand/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@
"next": "latest",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"zustand": "^3.5.4"
"zustand": "^3.7.1"
}
}
12 changes: 6 additions & 6 deletions examples/with-zustand/pages/ssg.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import Page from '../components/page'
import { initializeStore } from '../lib/store'

export default function SSG() {
return <Page />
}

// If you build and start the app, the date returned here will have the same
// value for all requests, as this method gets executed at build time.
// You will not see this while in development mode though.
export function getStaticProps() {
// Note that in this case we're returning the state directly, without creating
// the store first (like in /pages/ssr.js), this approach can be better and easier
const zustandStore = initializeStore()
return {
props: {
initialZustandState: {
lastUpdate: Date.now(),
light: false,
},
// the "stringify and then parse again" piece is required as next.js
// isn't able to serialize it to JSON properly
initialZustandState: JSON.parse(JSON.stringify(zustandStore.getState())),
},
}
}
3 changes: 0 additions & 3 deletions examples/with-zustand/pages/ssr.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ export default function SSR() {
// exported when you use `getServerSideProps` or `getInitialProps`
export function getServerSideProps() {
const zustandStore = initializeStore()

zustandStore.getState().tick(Date.now(), false)

return {
props: {
// the "stringify and then parse again" piece is required as next.js
Expand Down

0 comments on commit b2dffd2

Please sign in to comment.