Study
react
Usecontext

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>
);