-
-
Save stephenhuh/4e1c47ac2eee4fc2cfcb7b54b89874c6 to your computer and use it in GitHub Desktop.
Global React State with Context and Immer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { useCount, useIncrement, useDecrement } from './store.Count' | |
export default function Counter () { | |
const count = useCount() | |
const increment = useIncrement() | |
const decrement = useDecrement() | |
return ( | |
<div> | |
<div>Count: {count}</div> | |
<div> | |
<button onClick={increment}>Increment</button> | |
<button onClick={decrement}>Decrement</button> | |
</div> | |
) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react' | |
import { render } from 'react-dom' | |
// Import the store | |
import Store from './store' | |
// Import any initialState | |
import { initialState as count } from './store.Count' | |
// Import components | |
import Counter from './Counter' | |
// Build initial state | |
const initialState = { | |
count | |
} | |
render( | |
// Use the Store Provider | |
<Store.Provider initialState={initialState}> | |
<Counter /> | |
</Store.Provider | |
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useState, useMemo, useCallback, useContext } from 'react' | |
import immer from 'immer' // Immer rocks. Go check it out if you haven't | |
export default function makeStore() { | |
// This is basically a factory for a new store instance | |
// Create a new context for this store instance | |
const context = React.createContext() | |
// Make a provider that takes an initial state | |
const Provider = ({ children, initialState = {} }) => { | |
// useState will do. Nothing fancy needed | |
const [state, preSetState] = useState(initialState) | |
// We make our own setState callback that uses immer under the hood | |
// (or we could use `useImmer`, but this is fun, too) | |
const setState = useCallback( | |
updater => preSetState(old => immer(old, draft => updater(draft))), | |
preSetState | |
) | |
// Memoize the context value so it only updates when the state changes | |
const contextValue = useMemo(() => [state, setState], [state]) | |
// Pass the context down | |
return <context.Provider value={contextValue}>{children}</context.Provider> | |
} | |
// A hook to consume the context. No need to import `useContext` everywhere. How wasteful... | |
const useStore = () => useContext(context) | |
// Export them however you like. I prefer a default. | |
return { | |
Provider, | |
useStore, | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Store from './' | |
// Export our initial state | |
export const initialState = 0 | |
export function useCount () { | |
const [{ count }] = Store.useStore() | |
return count | |
} | |
export function useIncrement () { | |
const [_, setState] = Store.useStore() | |
return () => setState(draft => { | |
draft.count++ | |
}) | |
} | |
export function useDecrement () { | |
const [_, setState] = Store.useStore() | |
return () => setState(draft => { | |
draft.count-- | |
}) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Import our factory | |
import makeStore from './makeStore' | |
// Export a new state instance | |
export default makeStore() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment