props
를 여러 컴포넌트를 거쳐 값을 자식 컴포넌트에게 전달해주지 않고도, contextAPI 를 사용하면 컴포넌트가 어느 위치에 있던 다른 컴포넌트에게 바로 사용할 수 있도록 값을 전달해줄 수 있다. 이를 타입스크립트에서 활용하려고 한다.
타입 선언하기
// 필요한 타입들을 미리 선언
type Color = 'red' | 'orange' | 'yellow';
// 상태를 위한 타입
type State = {
count: number;
text: string;
color: Color;
isGood: boolean;
};
// 모든 액션들을 위한 타입
type Action =
| { type: 'SET_COUNT'; count: number }
| { type: 'SET_TEXT'; text: string }
| { type: 'SET_COLOR'; color: Color }
| { type: 'TOGGLE_GOOD' };
context 만들기
context 는 state
와 dispatch
2가지를 따로 생성하였다.
state
와 dispatch
를 각각 필요한 컴포넌트에서만 사용하도록 해서 불필요한 렌더링을 방지할 수 있다.
dispatch
의 경우 <Generic> 으로 dispatch 함수에 대한 타입을 작성해주어야 하는데,
이 때 React 에 있는 Dispatch
를 불러와 generic 으로 <Action>
을 작성해주면 된다.
type FooValue = {
foo: number;
}
// createContext<Generic 으로 관리하고 싶은 값 또는 null>(null(기본값));
const FooContext = createContext<FooValue | null>(null);
// context 생성
// 1. 상태를 다루는 context
const SampleStateContext = createContext<State | null>(null);
// 2. reducer 사용 시 dispatch 를 관리하는 context
const SampleDispatchContext = createContext<Dispatch<Action> | null>(null);
위의 구조가 복잡하다고 느껴지면 다음과 같이 정리할 수도 있다.
type SampleDispatch = Dispatch<Action>;
const SampleStateContext = createContext<State | null>(null);
const SampleDispatchContext = createContext<SampleDispatch | null>(null);
Provider 구현
SampleProvider 에서 useReducer 를 사용하고
SampleStateContext.Provider
와 SampleDispatchContext.Provider
로 children
을 감싸서 반환한다.
export function SampleProvider({ children }: SampleProviderProps) {
const [state, dispatch] = useReducer(reducer, {
count: 0,
text: 'hello',
color: 'red',
isGood: true
});
return (
// sample state 와 dispatch 감싸는 순서는 상관없다.
<SampleStateContext.Provider value={state}>
<SampleDispatchContext.Provider value={dispatch}>
{children}
</SampleDispatchContext.Provider>
</SampleStateContext.Provider>
)
}
커스텀 Hook 만들기
context
를 통해서 관리하고 있는 value
들을 다른 컴포넌트에서 사용하려면 useContext
를 사용해야 한다.
context 로 만든 state
와 dispatch
를 쉽게 사용할 수 있도록 커스텀 Hook 을 만들려고 한다.
이때 만들어준 커스텀 Hook 에서 useSampleState
와 useSampleDispatch
의 null 체킹을 해주는 것이 중요하다.
만약에 Context 가 가지고 있는 값이 유효하지 않으면 에러를 발생시키는 코드를 작성해준다.
이를 통해 커스텀 Hook 을 사용하게 될 때도 각 Hooks 함수들이 반환하는 값이 언제나 유효한 것을 보장할 수 있다.
export function useSampleState() {
const state = useContext(SampleStateContext);
if (!state) throw new Error('Cannot find SampleProvider'); // null 체킹
return state;
}
export function useSampleDispatch() {
const dispatch = useContext(SampleDispatchContext);
if (!dispatch) throw new Error('Cannot find SampleProvider'); // null 체킹
return dispatch;
}
이제 만든 hook 들을 가지고 이전에 만들었던 ReducerSample.tsx 파일에 적용해보면 이전의 코드와 동일하게 잘 적용된 것을 볼 수 있다.
import React from "react";
import { useSampleDispatch, useSampleState } from "./SampleContext";
function ReducerSample() {
// 커스텀 Hook 함수 적용
const state = useSampleState();
const dispatch = useSampleDispatch();
const setCount = () => dispatch({ type: 'SET_COUNT', count: 5 });
const setText = () => dispatch({ type: 'SET_TEXT', text: 'bye' });
const setColor = () => dispatch({ type: 'SET_COLOR', color: 'orange' });
const toggleGood = () => dispatch({ type: 'TOGGLE_GOOD' });
return (
<div>
<p>
<code>count: </code> {state.count}
</p>
<p>
<code>text: </code> {state.text}
</p>
<p>
<code>color: </code> {state.color}
</p>
<p>
<code>isGood: </code> {state.isGood ? 'true' : 'false'}
</p>
<div>
<button onClick={setCount}>SET_COUNT</button>
<button onClick={setText}>SET_TEXT</button>
<button onClick={setColor}>SET_COLOR</button>
<button onClick={toggleGood}>TOGGLE_GOOD</button>
</div>
</div>
);
}
export default ReducerSample;
이 때 이것을 렌더링하면 SampleProvider 를 사용하지 않아 오류가 뜬다.
App.tsx
SampleProvider
로 감싸준 뒤에 렌더링 하면 잘 작동되는 것을 확인할 수 있다.
const App: React.FC = () => {
return (
<SampleProvider>
<ReducerSample />
</SampleProvider>
);
}
이처럼 타입스크립트를 사용해 contextAPI 를 사용하는 것은 자바스크립트를 사용하는 것과 크게 차이나지는 않지만,
<Generic> 을 작성해주고, 커스텀 Hook 을 만들 때 null 체킹을 해주어야 한다는 차이가 있다.
'Typescript' 카테고리의 다른 글
타입스크립트에서 리덕스 사용하기 - 투두리스트 (0) | 2021.11.22 |
---|---|
타입스크립트에서 리덕스 사용하기 - 카운터 (0) | 2021.11.21 |
타입스크립트로 리액트 상태 관리 (0) | 2021.11.20 |
리액트 컴포넌트 타입스크립트로 작성하기 (0) | 2021.11.19 |
타입스크립트 Generics (0) | 2021.11.18 |