Skip to content

Instantly share code, notes, and snippets.

@BrianCodeItUp
Last active May 12, 2022 06:43
Show Gist options
  • Save BrianCodeItUp/c4743a5746210351408413a987ac085b to your computer and use it in GitHub Desktop.
Save BrianCodeItUp/c4743a5746210351408413a987ac085b to your computer and use it in GitHub Desktop.
useContext, useReducer with typescript
import React from 'react';
import { View, Text, Button } from 'react-native';
import styles from './styles'
import { ContextProvider, Context } from './context';
const Compo = () => {
const { type } = useContext(Context);
return (
<View style={styles.container}>
<Text>{type}</Text>
</View>
)
}
const WrappedComponent = () => {
return (
<ContextProvider>
<Compo />
</ContextProvider>
)
}
export default WrappedComponent;
import React, { useReducer, Dispatch, Reducer } from 'react';
import { produce, Draft } from 'immer';
/**
* 基本的 Action 物件
*/
interface Action {
type: string,
payload?: any
}
/**
* Context 裡的 dispatch
*/
interface ContextDispatch<Y> {
setContext: Dispatch<Y>
}
/**
* immer 的 reducer
*/
interface DraftReducer<X, Y> {
(this: Draft<X>, draft: Draft<X>, action: Y): X;
}
/**
* Context 建立器,回傳 Context 和其 Provider
* @param initialState - 初始 State
* @param reducer - immer 的 redcuer
*/
function contextCreator<X extends object, Y extends Action>(
initialState: X,
reducer: DraftReducer<X, Y>
) {
const Context = React.createContext<X & ContextDispatch<Y>>(null);
const ContextProvider = ({ children }) => {
const immerReducer = produce(reducer) as Reducer<X, Y>;
const [state, dispatch] = useReducer(immerReducer, initialState)
return(
<Context.Provider value={{ ...state, setContext: dispatch }}>
{children}
</Context.Provider>
)
}
return { Context, ContextProvider }
}
export default contextCreator;
import contextCreator from './contextCreator'
import { Draft } from 'immer'
/**
* create a context.ts file for all components in the component hierarchy can import Context
*/
interface State {
/**
* Menu 是否展開
*/
isMenuShown: boolean;
/**
* 打卡類型
*/
type: PunchCardType | ''
}
const initState: State = {
isMenuShown: false,
type: ''
};
interface Action {
type: 'CLOSE_MENU' | 'OPEN_MENU';
payload?: any;
}
const reducer = (draft: Draft<State>, action: Action) => {
switch (action.type) {
case 'OPEN_MENU':
draft.isMenuShown = true;
break;
case 'CLOSE_MENU':
draft.isMenuShown = false;
break;
default:
return draft;
}
}
const { Context, ContextProvider } = contextCreator<State, Action>(initState, reducer);
export {
Context,
ContextProvider,
}
@BrianCodeItUp
Copy link
Author

BrianCodeItUp commented Mar 16, 2020

Utility for binding useContext and useReducer together. I also add immer to make state change in reducer easier.
Step:

  • create a context.ts file then create your reducer and initial state there, export them for component to use.
  • import the ContextProvider and wrap the parent component in the component hierarchy
  • import the Context object and put it as parameter in useContext in order to access Context

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