-
[유데미x스나이퍼팩토리] 프로젝트 캠프 : Next.js 2기 - 사전직무교육 2주차 - Context프로젝트 캠프: Next.js 과정 2기 2024. 7. 26. 02:07728x90
💡 props drilling을 해결하기 위해 나온 함수로 데이터를 관리하는 곳을 따로 만들고 사용하여 props로 데이터를 전달하지 않아도 자식 컴포넌트가 부모 컴포넌트의 상태를 참조/업데이트할 수 있게 도와준다.
언제 사용 할까?
context는 리액트 컴포넌트 트리 안에서 전역적으로 사용할 수 있는 데이터를 공유할 수 있도록 나온 기능이다. 따라서
- 로그인한 유저
- 테마
- 선호하는 언어
등과 같이 앱 안에서 전역적으로 사용되는 데이터에 사용하는 경우가 많다.
기본 형태
const StateContext = createContext(기본값);
컴포넌트 외부에서 createContext를 호출해 커텍스트를 사용합니다.
- 기본값: 컴포넌트가 컨텍스트를 읽을 때 상위에 일치하는 컨텍스트 제공자가 없는 경우 컨텍스트가 가져야 할 값
- 기본값이 없는 경우 null 지정
- 시간이 지나도 변하지 않음.
컨텍스트 자체는 어떠한 정보도 가지고 있지 않으며, 다른 컴포넌트가 읽거나 제공하는 어떤 컨텍스트를 나타낸다.
- StateContext.Provider
- 상위 컴포넌트에서 컨텍스트 값을 지정
- StateContext.Consumer
- 하위 컴포넌트에서 데이터를 읽기 위해 호출
- 드물게 사용됨
사용 방법
예제 코드
import { useState } from 'react'; import './App.css'; import Page from './Page'; function App() { const [isLightMode, setIsLightMode] = useState(true); return ( <> <Page isLightMode={isLightMode} setIsLightMode={setIsLightMode} /> </> ); } export default App;
import ThemeButton from './ThemeButton'; import TopBar from './TopBar'; interface IPageProps { isLightMode: boolean; setIsLightMode: React.Dispatch<React.SetStateAction<boolean>>; } const Page = ({ isLightMode, setIsLightMode }: IPageProps) => { return ( <> <TopBar isLightMode={isLightMode} /> <ThemeButton setIsLightMode={setIsLightMode} /> </> ); }; export default Page;
이렇게 라이트모드/다크모드를 구분할 수 있는 상태를 props로 계속 아래로 넘겨주고 있다. 현재는 컴포넌트의 깊이가 깊은 편은 아니지만 만약 여기서 더 깊어진다면 상태변화로 인해 재렌더링되는 컴포넌트의 수가 많아져 성능적인 문제가 발생할 수 있다.
context 적용
export const ThemeContext = createContext<{ isLightMode: boolean; setIsLightMode: React.Dispatch<React.SetStateAction<boolean>>; }>({ isLightMode: true, setIsLightMode: () => {} });
- 기본값: { isLightMode: true, setIsLightMode: () => {} }
- 기본값이 객체이기 때문에 타입스크립트에서는 타입 오류가 발생할 수 있다. 따라서 createContext 옆에 제네릭으로 타입을 지정해줘야 한다.
import { useState } from 'react'; import './App.css'; import Page from './Page'; import { ThemeContext } from './context/ThemeContext'; function App() { const [isLightMode, setIsLightMode] = useState(true); return ( <ThemeContext.Provider value={{ isLightMode, setIsLightMode }}> <Page /> </ThemeContext.Provider> ); } export default App;
import ThemeButton from './ThemeButton'; import TopBar from './TopBar'; const Page = () => { return ( <> <TopBar /> <ThemeButton /> </> ); }; export default Page;
이제 이렇게 context의 provider를 적용하면 props로 상태값을 넘겨주지 않아도 된다.
그럼 데이터를 어떻게 가져와서 사용할 수 있을까?
const { isLightMode } = useContext(ThemeContext);
💡 이렇게 useContext라는 훅을 사용해주면 된다!
memoization
Page 컴포넌트에 컨텍스트 데이터를 사용하지 않는 컴포넌트를 하나 추가해보도록 하겠다.
const None = () => { console.log('no!!'); return <>No Contents</>; }; export default None;
import None from './None'; import ThemeButton from './ThemeButton'; import TopBar from './TopBar'; const Page = () => { return ( <> <TopBar /> <ThemeButton /> <None /> </> ); }; export default Page;
위의 코드를 실행하고 테마를 변동시키면 컨텍스트 데이터를 사용하고 있지 않는 None 컴포넌트도 재렌더링이 되는 것을 확인할 수 있다.
왜??
상위 컴포넌트인 App 컴포넌트에서 정의한 isLightMode 상태가 업데이트됐기 때문에 하위 컴포넌트인 None도 자동으로 재렌더링된 것이다.
이걸 어떻게 해결할까?
memoization을 추가하면 된다! 간단하게 React.memo 함수를 사용해도 되지만 그 방법은 생각보다 많은 비용을 요구한다고 한다. 따라서 컨텍스틀 이용해 재렌더링을 방지하는 코드를 작성해보자.
export const CounterContextProvider = ({ children, }: { children: React.ReactNode; }) => { const [count, setCount] = useState(0); return ( <CounterContext.Provider value={{ count, setCount }}> {children} </CounterContext.Provider> ); };
import './App.css'; import Page from './Page'; import { CounterContextProvider } from './context/ThemeContext'; function App() { return ( <CounterContextProvider> <Page /> </CounterContextProvider> ); } export default App;
위와 같이 children을 받아서 context provider내부에 넣어주는 함수 컴포넌트를 제작하면 memoization을 사용하지 않아도 재렌더링을 방지할 수 있다.
어떻게 가능한걸까?
React.memo() 함수는 함수 컴포넌트에서 컴포넌트를 반환하는 고차 컴포넌트 형태이다. 위의 코드 또한 함수 컴포넌트에서 컴포넌트를 반환하는 코드로 작성한 것이기 때문에 React.memo()처럼 동작해 별도로 memoization을 적용하지 않아도 재렌더링을 막을 수 있다.
예제 코드
https://stackblitz.com/edit/vitejs-vite-g5rggb?file=src%2FApp.tsx
728x90'프로젝트 캠프: Next.js 과정 2기' 카테고리의 다른 글
[유데미x스나이퍼팩토리] 프로젝트 캠프 : Next.js 2기 - 사전직무교육 2주차 후기 (0) 2024.07.28 [유데미x스나이퍼팩토리] 프로젝트 캠프 : Next.js 2기 - 사전직무교육 2주차 - Memoization (0) 2024.07.28 [유데미x스나이퍼팩토리] 프로젝트 캠프 : Next.js 2기 - 사전직무교육 2주차 - useReducer (0) 2024.07.26 [유데미x스나이퍼팩토리] 프로젝트 캠프 : Next.js 2기 - 사전직무교육 2주차 - 리액트 hook (0) 2024.07.25 [유데미x스나이퍼팩토리] 프로젝트 캠프 : Next.js 2기 - 사전직무교육 2주차 - React 조건부 렌더링과 반복 렌더링 (0) 2024.07.25