전역 상태가 아닌 특정 컴포넌트의 상태를 관리할 때는 context API를 많이 활용한다.
이를 좀 더 편하게 사용하기 위해 아래와 같은 형태로 context 를 만들어서 사용한다.
// context/management
import React, { useContext, useEffect } from "react"
import { SOME_TYPE } from "./type/management";
interface IManagementState {}
interface IManagementAction {
type:string;
payload?:any;
}
const initState:IManagementState = {};
export const managementReducer = (state:IManagementState, action:IManagementAction) => {
switch(action.type) {
case SOME_TYPE : {
// do something
// ..
return state;
}
default:
return state;
}
}
const managementContext = () => {
const ctx = React.createContext<{
state:IManagementState
dispatch:React.Dispatch<IManagementAction>
}>({
state:initState,
dispatch:() => null
})
const useCtx = () => {
const c = useContext(ctx);
return c;
}
return [
ctx.Provider,
useCtx,
initState
] as const;
}
export default managementContext;
// context/type/management
export const SOME_TYPE = 'SOME_TYPE';
// page
import managementContext, { managementReducer } from '@/context/state'
import { useReducer } from 'react';
export const [ManagementContextProvider, useManagementContext, initManagementContextState] = managementContext();
export default function Page() {
const [state, dispatch] = useReducer(managementReducer, initManagementContextState);
return (
<ManagementContextProvider
value={{
state,
dispatch
}}>
{/* ... */}
</ManagementContextProvider>
)
}
Context Provider 를 state, dispatch 2개 각각 만들어서 성능최적화를 시켜줄 수 있지만.. 패스
context 의 상태는 reducer 를,
useCtx 함수를 통해 useContext 객체를 외부로,
useReducer 를 통해 context reducer 와 context initState 를,
이를 Provider를 통해 Context API 를 사용할 수 있도록 하는 구조이다
하위 컴포넌트에서는 아래와 같이 export 한 useContext 객체를 손쉽게 사용할 수 있다
const { state, dispatch } = useManagementContext()
최근에 서로 다른 페이지에서 동일한 형태의 뷰를 갖는 컴포넌트를 추가해야 했는데,
기존의 컴포넌트는 위의 구조로 상태관리를 하고 있었다
기존에 사용하던 view 단의 context 가 위와 같이 managementContext 였다면
사실 managementContext2 로 복사해서 사용하면 그만이지만..
또 언제 같거나 비슷한 형태의 뷰를 추가해야할지 모르기에
HOC 를 이용하면 아래와 같이 간단한 수정으로 컨텍스트의 중복 없이 같은 구조의 Context 를 사용할 수 있다
// hoc/management.tsx
import managementContext, { managementReducer } from "@/context/state";
import { PropsWithChildren, useReducer } from "react";
export const [ManagementContextProvider, useManagementContext, initManagementContextState] = managementContext();
const ManagementContextHOC = ({ children }:PropsWithChildren<{}>) => {
const [state, dispatch] = useReducer(managementReducer, initManagementContextState);
return (
<ManagementContextProvider
value={{state, dispatch}}>
{children}
</ManagementContextProvider>
)
}
export default ManagementContextHOC;
// page1.tsx
import ManagementContextHOC from '@/hoc/management';
export default function Page1() {
return (
<ManagementContextHOC>
<SomeComponent />
</ManagementContextHOC>
)
}
// page2.tsx
import ManagementContextHOC from '@/hoc/management';
export default function Page2() {
return (
<ManagementContextHOC>
<SomeComponent />
</ManagementContextHOC>
)
}
context api + useReducer + HOC 를 이용한 위와 같은 구조는
무분별한 전역 상태 관리가 아닌
필요한 뷰 단에서의 상태관리를 구분짓기에 좋은 구조라고 생각한다
'web > React' 카테고리의 다른 글
digital envelope routines::unsupported (0) | 2023.06.20 |
---|---|
React-Query Dynamic Parallel Queries (0) | 2023.06.06 |
제너레이터(Generator) (1) | 2021.01.11 |
Redux 비동기(redux-saga) (0) | 2021.01.11 |
리액트 내장 훅 (0) | 2021.01.01 |