Last active
May 12, 2022 06:43
-
-
Save BrianCodeItUp/c4743a5746210351408413a987ac085b to your computer and use it in GitHub Desktop.
useContext, useReducer with typescript
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 { 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; |
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, { 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; |
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 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, | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Utility for binding useContext and useReducer together. I also add
immer
to make state change in reducer easier.Step:
ContextProvider
and wrap the parent component in the component hierarchyContext
object and put it as parameter inuseContext
in order to access Context