Skip to content

Instantly share code, notes, and snippets.

@bryanltobing
Last active April 22, 2024 12:57
Show Gist options
  • Save bryanltobing/e09cb4bb110c4d10cefde665b572d899 to your computer and use it in GitHub Desktop.
Save bryanltobing/e09cb4bb110c4d10cefde665b572d899 to your computer and use it in GitHub Desktop.
ViewModelProvider with zustand and react context
import { createContext, useContext, useRef } from 'react'
import { Button, Text } from 'tamagui'
import { createStore, useStore, type StateCreator, type StoreApi } from 'zustand'
const ViewModelContext = createContext<StoreApi<any> | null>(null)
const ViewModelProvider = <T,>({
children,
initializer,
}: React.PropsWithChildren<{ initializer: StateCreator<T> }>) => {
const storeRef = useRef<StoreApi<T>>()
if (!storeRef.current) {
storeRef.current = createStore(initializer)
}
return (
<ViewModelContext.Provider value={storeRef.current}>
{children}
</ViewModelContext.Provider>
)
}
const useViewModelContext = <T, U>(selector: (state: T) => U): U => {
const viewModelContextValue = useContext(ViewModelContext)
if (!viewModelContextValue) throw new Error('Missing ViewModelContext.Provider in the tree')
return useStore(viewModelContextValue, selector)
}
type StoreInitializer = {
bears: number
increasePopulation: () => void
}
const TextChild = () => {
const bears = useViewModelContext((state: StoreInitializer) => state.bears)
return <Text>{bears}</Text>
}
const ButtonChild = () => {
const increasePopulation = useViewModelContext(
(state: StoreInitializer) => state.increasePopulation
)
return <Button onPress={increasePopulation}>Increase Population</Button>
}
export const App = () => {
return (
<ViewModelProvider<StoreInitializer>
initializer={(set) => ({
bears: 0,
increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
})}
>
<TextChild />
<ButtonChild />
</ViewModelProvider>
)
}
@bryanltobing
Copy link
Author

without examples

import { createContext, useContext, useRef } from 'react'
import { createStore, useStore, type StateCreator, type StoreApi } from 'zustand'

const ViewModelContext = createContext<StoreApi<any> | null>(null)

export const ViewModelProvider = <T,>({
  children,
  initializer,
}: React.PropsWithChildren<{ initializer: StateCreator<T> }>) => {
  const storeRef = useRef<StoreApi<T>>()
  if (!storeRef.current) {
    storeRef.current = createStore(initializer)
  }
  return (
    <ViewModelContext.Provider value={storeRef.current}>
      {children}
    </ViewModelContext.Provider>
  )
}

export const useViewModelContext = <T, U>(selector: (state: T) => U): U => {
  const viewModelContextValue = useContext(ViewModelContext)
  if (!viewModelContextValue)
    throw new Error('Missing ViewModelContext.Provider in the tree')

  return useStore(viewModelContextValue, selector)
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment