Last active
September 13, 2022 06:55
-
-
Save pjchender/afb3149f7f0b5ef4149adc84b4ff51f4 to your computer and use it in GitHub Desktop.
useContext + useReducer with TypeScript
This file contains hidden or 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 * as React from 'react'; | |
import { ChangeEvent } from 'react'; | |
import { ColorSlider } from './ColorSlider'; | |
import { useContext } from './rgb-context'; | |
export interface AdjustmentInputProps | |
extends React.HTMLProps<HTMLInputElement> { | |
id: string; | |
label: string; | |
value: number; | |
onChange: (event: ChangeEvent<HTMLInputElement>) => void; | |
} | |
export interface ColorAdjustmentProps { | |
Adjustment: React.ComponentType<AdjustmentInputProps>; | |
} | |
export const ColorAdjustment = ({ Adjustment }: ColorAdjustmentProps) => { | |
// STEP 6:從 createContext utility 中取出該 context 內的資料 | |
const { red, green, blue, dispatch } = useContext(); | |
const adjustRed = (event: ChangeEvent<HTMLInputElement>) => { | |
dispatch({ type: 'ADJUST_RED', payload: +event.target.value }); | |
}; | |
const adjustGreen = (event: ChangeEvent<HTMLInputElement>) => { | |
dispatch({ type: 'ADJUST_GREEN', payload: +event.target.value }); | |
}; | |
const adjustBlue = (event: ChangeEvent<HTMLInputElement>) => { | |
dispatch({ type: 'ADJUST_BLUE', payload: +event.target.value }); | |
}; | |
return ( | |
<section className="color-sliders"> | |
<Adjustment | |
id="red-slider" | |
label="Red" | |
value={red} | |
onChange={adjustRed} | |
/> | |
<Adjustment | |
id="green-slider" | |
label="Green" | |
value={green} | |
onChange={adjustGreen} | |
/> | |
<Adjustment | |
id="blue-slider" | |
label="Blue" | |
value={blue} | |
onChange={adjustBlue} | |
/> | |
</section> | |
); | |
}; |
This file contains hidden or 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
// create-context.tsx | |
// https://frontendmasters.com/courses/react-typescript/context-api-edge-cases/ | |
// STEP 1:建立 createContext 這個 utility | |
// <A extends {} | null>:A 需要滿足是任何的物件,否則是 null | |
export function createContext<A extends {} | null>() { | |
const ctx = React.createContext<A | undefined>(undefined); | |
const useContext = () => { | |
const c = React.useContext(ctx); | |
if (c === undefined) { | |
throw new Error('useContext must be inside a Provider with a value'); | |
} | |
return c; | |
}; | |
// as const:讓這個 tuple 是 read only | |
return [useContext, ctx.Provider] as const; | |
} |
This file contains hidden or 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 './style.scss'; | |
import { render } from 'react-dom'; | |
import Application from './Application'; | |
import { RGBContextProvider } from './rgb-context'; | |
import { ThemeProvider } from './theme-context'; | |
const rootElement = document.getElementById('root'); | |
// STEP 5:使用 RGBContextProvider | |
render( | |
<ThemeProvider> | |
<RGBContextProvider> | |
<Application /> | |
</RGBContextProvider> | |
</ThemeProvider>, | |
rootElement | |
); |
This file contains hidden or 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
// https://frontendmasters.com/courses/react-typescript/context-api-edge-cases/ | |
// STEP 3:撰寫 reducer | |
import { RGBColorType } from './types'; | |
const colors = ['red', 'green', 'blue'] as const; | |
type Colors = Uppercase<typeof colors[number]>; | |
type ActionTypes = `ADJUST_${Colors}`; // "ADJUST_RED" | "ADJUST_GREEN" | "ADJUST_BLUE" | |
export type AdjustmentAction = { | |
type: ActionTypes; | |
payload: number; | |
}; | |
export const reducer = ( | |
state: RGBColorType, | |
action: AdjustmentAction | |
): RGBColorType => { | |
for (const color of colors) { | |
if (action.type === `ADJUST_${color.toUpperCase()}`) { | |
return { ...state, [color]: action.payload }; | |
} | |
} | |
return state; | |
}; |
This file contains hidden or 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
// contexts/RGBContext.tsx | |
// https://frontendmasters.com/courses/react-typescript/context-api-edge-cases/ | |
import * as React from 'react'; | |
import { createContext } from './create-context'; | |
import { AdjustmentAction, reducer } from './reducer'; | |
import { RGBColorType } from './types'; | |
interface RGBContextType extends RGBColorType { | |
dispatch: React.Dispatch<AdjustmentAction>; | |
} | |
// STEP 2 使用 createContext uility 來建立 context 和 Provider | |
// 透過我們客製化的 createContext 來讓 TS 知道 context 的型別 | |
export const [useContext, Provider] = createContext<RGBContextType>(); | |
// 原本的作法是使用 React 本身的 createContext,並透過 as 來指定 initialState 的型別 | |
// export const RGBContext = React.createContext<RGBContextType>( | |
// initialState as RGBContextType | |
// ); | |
// const Provider = RGBContext.Provider; | |
export const RGBContextProvider = ({ | |
children, | |
}: { | |
children: React.ReactNode; | |
}) => { | |
// STEP 4:在 provider 中使用 useReducer,取得 reducer 的資料 | |
const [rgb, dispatch] = React.useReducer(reducer, { | |
red: 0, | |
green: 0, | |
blue: 0, | |
}); | |
return ( | |
<Provider | |
value={{ | |
...rgb, | |
dispatch, | |
}} | |
> | |
{children} | |
</Provider> | |
); | |
}; |
This file contains hidden or 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
export interface RGBColorType { | |
red: number; | |
green: number; | |
blue: number; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment