useContext
useContext가 등장한 이유 (a.k.a React context)
→ 계속 컴포넌트를 통해 전달하는 것이 번거로움
→ useContext 등장
but
자주 남발하지 말 것
- Provider로 부터 Consumer까지 direct로 value(상태) 공유! (ex. App →→ Profile)
- Provider의 Children들은 해당 context의 상태를 바로 접근 가능!
- Context.Provider ~ Context.Consumer
useContext 사용하기
createContext(기본값)
: 컨텍스트 생성- 컴포넌트 외부에서
createContext
를 호출하여 컨텍스트를 생성
- 컴포넌트 외부에서
SomeContext.Provider
: 컨텍스트 영역 지정- createContext를 통해 생성한 컨텍스트의 범위를 Provider를 통해 지정
- 컴포넌트를 컨텍스트 제공자로 감싸서 이 컨텍스트의 값을 모든 내부 컴포넌트에 지정
- 쉽게 이해하기 ) 제공하는 쪽
SomeContext.Consumer
: 컨텍스트 받기- 쉽게 이해하기 ) 사용하는 쪽
값을 공유하는 context
// context pattern 2 (provider-useContext)
export const MyContext = createContext();
render(
<MyContext.Provider value={{xObj}}>
<App />
</MyContext.Provider>
);
// context consumer by Provider + useContext
import { useContext } from 'react';
const { xObj } = useContext(MyContext);
상태를 공유하는 context
-
상태 변경 시, Provider는 물론, 모든 자식 컴포넌트(Consumer)는 모두 re-render됨
⇒ setState를 바로 공유하지 말고, 상태를 변경하는 함수를 전달하자!
ex) setCount (X) → plusCount 노출
BAD CODE
src/hooks/counter-context.tsx
// context pattern 3 (state-provider) BAD!
type CounterContextProps = {
count: number;
setCount: Dispatch<SetStateAction<number>>;
};
const CounterContext = createContext<CounterContextProps>({
count: 0,
setCount: () => {},
});
export const CounterProvider = ({ children }: PropsWithChildren) => {
const [count, setCount] = useState(0);
return (
<CounterContext.Provider value={{ count, setCount }}>
{children}
</CounterContext.Provider>
);
};
// eslint-disable-next-line react-refresh/only-export-components
export const useCounter = () => useContext(CounterContext);
src/main.tsx
// src/main.tsx - Provider
...
import { CounterProvider } from './hooks/counter-context.tsx';
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<CounterProvider>
<App />
</CounterProvider>
</React.StrictMode>
);
GOOD CODE
src/hooks/counter-context.tsx
// pattern4 - src/hooks/counter-context.tsx GOOD!
type CounterContextProps = {
count: number;
plusCount: () => void;
};
const CounterContext = createContext<CounterContextProps>({
count: 0,
plusCount: () => {},
});
export const CounterProvider = ({ children }: PropsWithChildren) => {
const [count, setCount] = useState(0);
const plusCount = () => setCount((count) => count + 1);
return (
<CounterContext.Provider value={{ count, **plusCount** }}>
{children}
</CounterContext.Provider>
);
};
// eslint-disable-next-line react-refresh/only-export-components
export const useCounter = () => useContext(CounterContext);
src/App.tsx
// Consumer
...
import { useCounter } from './hooks/counter-context';
function App() {
const { count, plusCount } = useCounter();
return (...);
}
export default App;
사용 예시
./hooks/counter-context
import { createContext, PropsWithChildren, useContext, useState } from 'react';
type CounterContextProps = {
count: number;
plusCount: () => void;
};
const CounterContext = createContext<CounterContextProps>({
count: 0,
plusCount: () => {},
});
export const CounterProvider = ({ children }: PropsWithChildren) => {
const [count, setCount] = useState(0);
const plusCount = () => setCount((preCount) => preCount + 1);
return (
<CounterContext.Provider value={{ count, plusCount }}>
{children}
</CounterContext.Provider>
);
};
export const useCounter = () => useContext(CounterContext);
./App.tsx
...
import { useCounter } from './hooks/counter-context';
function App() {
const { count, plusCount } = useCounter();
...
return (
<>
<div className='card'>
<button onClick={() => plusCount()}>count is {count}</button>
</div>
</>
);
}
export default App;
./main.tsx
...
import { CounterProvider } from './hooks/counter-context.tsx';
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<CounterProvider>
<App />
</CounterProvider>
</React.StrictMode>
);