React Context TS
Last active
March 28, 2023 12:49
-
-
Save GGrassiant/36f01e8c1b1c6f069fee9cc32706892d to your computer and use it in GitHub Desktop.
React Context TS
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
(David Gray) | |
import { createContext, useReducer, ChangeEvent, ReactElement, useCallback, useContext } from "react" | |
type StateType = { | |
count: number; | |
text: string; | |
} | |
export const initState: StateType = { count: 0, text: '' } | |
const enum REDUCER_ACTION_TYPE { | |
INCREMENT, | |
DECREMENT, | |
NEW_INPUT, | |
} | |
type ReducerAction = { | |
type: REDUCER_ACTION_TYPE, | |
payload?: string, | |
} | |
const reducer = (state: StateType, action: ReducerAction): StateType => { | |
switch (action.type) { | |
case REDUCER_ACTION_TYPE.INCREMENT: | |
return { ...state, count: state.count + 1 } | |
case REDUCER_ACTION_TYPE.DECREMENT: | |
return { ...state, count: state.count - 1 } | |
case REDUCER_ACTION_TYPE.NEW_INPUT: | |
return { ...state, text: action.payload ?? '' } | |
default: | |
throw new Error() | |
} | |
} | |
const useCounterContext = (initState: StateType) => { | |
const [state, dispatch] = useReducer(reducer, initState) | |
const increment = useCallback(() => dispatch({ type: REDUCER_ACTION_TYPE.INCREMENT }), []) | |
const decrement = useCallback(() => dispatch({ type: REDUCER_ACTION_TYPE.DECREMENT }), []) | |
const handleTextInput = useCallback((e: ChangeEvent<HTMLInputElement>) => { | |
dispatch({ | |
type: REDUCER_ACTION_TYPE.NEW_INPUT, | |
payload: e.target.value | |
}) | |
}, []) | |
return { state, increment, decrement, handleTextInput } | |
} | |
type UseCounterContextType = ReturnType<typeof useCounterContext> | |
const initContextState: UseCounterContextType = { | |
state: initState, | |
increment: () => { }, | |
decrement: () => { }, | |
handleTextInput: (e: ChangeEvent<HTMLInputElement>) => { }, | |
} | |
export const CounterContext = createContext<UseCounterContextType>(initContextState) | |
type ChildrenType = { | |
children?: ReactElement | ReactElement[] | undefined | |
} | |
export const CounterProvider = ({ | |
children, ...initState | |
}: ChildrenType & StateType): ReactElement => { | |
return ( | |
<CounterContext.Provider value={useCounterContext(initState)}> | |
{children} | |
</CounterContext.Provider> | |
) | |
} | |
type UseCounterHookType = { | |
count: number, | |
increment: () => void, | |
decrement: () => void, | |
} | |
export const useCounter = (): UseCounterHookType => { | |
const { state: { count }, increment, decrement } = useContext(CounterContext) | |
return { count, increment, decrement } | |
} | |
type UseCounterTextHookType = { | |
text: string, | |
handleTextInput: (e: ChangeEvent<HTMLInputElement>) => void, | |
} | |
export const useCounterText = (): UseCounterTextHookType => { | |
const { state: { text }, handleTextInput } = useContext(CounterContext) | |
return { text, handleTextInput } | |
} | |
// To be used like <CounterProvider count={initState.count} text={initState.text}> |
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
// Another way to handle Context in Typescript | |
export const useSomething = () => { | |
const context = useContext(SomethingContext); | |
if (!context) { | |
throw new Error('useSomething must be used within a SomethingProvider'); | |
} | |
return context; | |
}; | |
interface SomethingProviderProps { | |
children: React.ReactNode; | |
loader?: JSX.Element; | |
} | |
interface SomethingContextData { | |
something: Something; | |
} | |
export const SomethingContext = createContext<SomethingContextData | undefined>(undefined); | |
export function SomethingProvider({ children, loader = <AppLayoutLoader /> }: SomethingProviderProps) { | |
const { data, isLoading, isError } = useSomethingQuery(); | |
const value = useMemo(() => { | |
if (data) { | |
return { data }; | |
} | |
return undefined; | |
}, [data]); | |
if (isLoading) return loader; | |
if (isError || !value) return <ErrorMessage />; | |
return <SomethingContext.Provider value={value}>{children}</SomethingContext.Provider>; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment