useContext (typescript)
useContext
props를 app.tsx에서 각각 내려준다고 가정하자
이처럼 필요 없는 props를 내려주거나 하는 일이 발생할 수 있다.
props에 해당되는 값들을 따로 모아 context를 만들어 주어 context 에서 직접적으로 props를 제공하는 역할을 하는 것이 useContext이다
context 셋업
특정 contextProvider 안에 있는 모든 childeren에서 theme라는 prop을 사용한다고 가정하자
theme props
/* theme.ts */
export const theme = {
primary: {
main: '#3f5185',
text: '#fff',
},
secondary: {
main: '#f50057'.
text: '#fff',
},
};
ThemeContextProvider
/* ThemeContext.tsx */
import { createContext, ReactNode } from 'react';
import { theme } from './theme';
type ThemeContextProviderProps = {
children: ReactNode
};
export const ThemeContext = createContext(theme);
export const ThemeContextProvider = ({ children, } : ThemeContextProviderProps) => {
return (
<ThemeContext.Provider
value = {theme}>
children
</ThemeContext.Provider>
);
};
이렇게 작성해 준 ThemeContext Provider를 Prop(theme)을 제공하고 싶은 component의 부모요소로 넣어주면 된다
이 예제에서는 App.tsx의 자식 요소인 Box component를 작성하여 props를 제공하도록 한다
Box Component
export const Box = () => {
return (
<>
<div
// style={{background color: theme.primary.main,
// color: theme.primary.text,
>
Primary Theme Context
</div>
<div
// style={{background color: theme.secondary.main,
// color: theme.secondary.text,
>
Secondary Theme Context
</div>
</>
);
};
App.tsx
import { ThemeContextProvider } from './components/context/ThemeContext';
import { Box } from './components/Box';
export const App = () => {
return (
<div>
<ThemeContextProvider>
<Box />
<ThemeContextProvider>
</div>
);
};
Box 컴포넌트에 context 적용하기
useContext를 이용, Box 컴포넌트 내부에서 불러오면 된다
/* BOX COMPONENT useContext APPLY */
import { useContext } from 'react'
import { ThemeContext } from './context/ThemeContext'
export const Box = () => {
const theme = useContext(ThemeContext);
return (
<>
<div
className='container mb-2'
style={{
backgroundColor: theme.primary.main,
color: theme.primary.text,
}}
>
Primary Theme Context
</div>
<div
className='container'
style={{
backgroundColor: theme.secondary.main,
color: theme.secondary.text,
}}
>
Secondary Theme Context
</div>
</>
);
};
기존 Box 컴포넌트에 const theme = useContext(ThemeContext) 선언 후 theme.~~ 으로 접근하면 사용 가능하다
값을 미래에 받는 경우
위의 예시에서는 theme을 통해 값을 받은 후 props를 뿌려줬다.
지금 값이 정해지지 않은 경우 useContext를 사용하는 방법을 알아보자
특정 유저를 인증해야하는 상황이라 가정한다
AuthUser type
/* AUTHUSER TYPE */
export type AuthUser = {
name: string,
email: string,
};
다른 컴포넌트에서도 사용 가능하도록 export를 붙여 줬다
미래에 값이 정해지는 UserContext 정의
/* UsetContext.tsx */
import { useState, createContext, ReactNode } from 'react';
export type AuthUser = {
name: string;
email: string;
};
type UserContextProviderProps = {
children: ReactNode;
};
export const UserContext = createContext(null);
export const UserContextProvider = ({ children }: UserContextProviderProps) => {
const [user, setUser] = useState<AuthUser>({} as AuthUser);
return (
<UserContext.Provider value={{ user, setUser }}>
{children}
</UserContext.Provider>
);
};
미래에 값이 정해지므로 createContext에 null 값을 전달했고, 이후 결정된 값이 user에 setUser을 통해 저장되도록 했다
error message image
null 은 React.Dispatch<React.SetStateAction> 형식에 들어갈 수 없는 값이라는 에러가 난다
해결하기 위해서 UserContextType을 추가하자.
/* UserContext type */
type UserContextType = {
user: AuthUser | null;
setUser: React.Dispatch<React.SetStateAction<AuthUser>>;
};
문제가 된 setUser에 type을 줬다.
이후 type assertion을 이용하여 null값 사용을 제거한다
export const UserContext = createContext({} as UserContextType);
전체 코드
import { useState, createContext, ReactNode } from 'react';
export type AuthUser = {
name: string;
email: string;
};
type UserContextType = {
user: AuthUser;
setUser: React.Dispatch<React.SetStateAction<AuthUser>>;
};
type UserContextProviderProps = {
children: ReactNode;
};
export const UserContext = createContext({} as UserContextType);
export const UserContextProvider = ({ children }: UserContextProviderProps) => {
const [user, setUser] = useState<AuthUser>({} as AuthUser);
return (
<UserContext.Provider value={{ user, setUser }}>
{children}
</UserContext.Provider>
);
};
에러가 없어진 모습을 볼 수 있다
이전 box component에 적용했던 것 처럼 적용하면 된다
적용예시
import { useContext } from 'react';
import { AuthUser, UserContext } from './context/UserContext';
export const User = () => {
const userContext = useContext(UserContext);
const handleLogin = () => {
userContext.setUser({
name: 'joe',
email: 'joe@gmail.com',
});
};
const handleLogout = () => {
userContext.setUser({} as AuthUser);
};
return (
<div className='container'>
<div className='input-div'>
<button className='button' onClick={handleLogin}>
Login
</button>
<button className='button' onClick={handleLogout}>
Logout
</button>
</div>
<div className='output-div'>
<div>User name is {userContext.user.name}</div>
<div>User email is {userContext.user.email}</div>
</div>
</div>
);
};