import React, { useCallback, useContext, useMemo, useRef, useState, } from "react"; /** This is a performance optimization. It allows using state throughout the * component tree while minimizing rerenders * */ export const createStateContext = (initialValue: T) => { const stateContext = React.createContext(initialValue); const staticContext = React.createContext<{ setState: React.Dispatch>; ref: { current: T }; }>({ setState: () => {}, ref: { current: initialValue }, }); return { // useState is used when you want to have the state value and you need to // rerender your component every time the state value changes // eg. when the state changes how a component renders useState: () => useContext(stateContext), // useRef is used when you want to have the state value and don't want to // rerender your component when the state value changes // eg. when the state is used in a callback useRef: () => useContext(staticContext).ref as { readonly current: T }, // useSetter to update the state value. This value should never change so it // won't cause rerenders. useSetter: () => { const { setState, ref } = useContext(staticContext); return useCallback( (action: React.SetStateAction) => { const newValue = typeof action === "function" ? (action as (val: T) => T)(ref.current) : action; ref.current = newValue; setState(newValue); }, [setState, ref] ); }, Provider: ({ children }: { children: React.ReactNode }) => { const [state, setState] = useState(initialValue); const ref = useRef(initialValue); const staticValue = useMemo(() => ({ setState, ref }), []); return ( {children} ); }, }; };