useContext훅에 대해 알아보자

0
post-thumbnail
post-custom-banner

❓ Context API란

: React가 자체적으로 제공하는 상태 관리 방법으로 컴포넌트 트리 안에서 전역적으로 사용할 수 있는 값을 관리할 수 있다. 주로 사용되는 예시로는 로그인한 사용자의 정보, 테마(다크모드), 언어 설정(다국어 처리 구현) 등이 있다.


🔹장점

  • Props Drilling을 피하고 컴포넌트 간 상태를 쉽게 공유 가능하게 한다.

  • 추가적인 라이브러리를 설치할 필요가 없다.

자식 요소 컴포넌트가 컴포넌트 간에 공유되는 데이터를 전달 받기 위해 props로 모든 상위 컴포넌트를 거쳐야 했던 props drilling 방식은 매우 복잡하며 전달받는 props가 많아질 경우 오류가 발생할 가능성도 높아지는 등 비효율적이다.

context api를 사용하면 부모 컴포넌트로부터 props를 전달받지 않고 전역적으로 데이터를 받아올 수 있게된다.



✔️ 적용하기

1️⃣ createContext로 Context 생성하기

기본형 > const SomeContext = createContext(defaultValue)
: createContext 내부에 공유하길 원하는 데이터의 초깃값을 넣어둔다.

import React, { createContext, useMemo, useState } from 'react';

export const UserContext = createContext({
  setLoggedIn: () => {},
  setLoading: () => {},
});

2️⃣ 생성된 context를 이용하여 provider로 컴포넌트 트리를 감싸기

: 넣어둔 초깃값을 value 변수로 묶어준다. 이 때 value 객체는 객체이기 때문에 리렌더링의 주범이 되므로 useMemo로 캐싱해두어야 한다. (메모이제이션 해두지 않으면 나중에 이 데이터를 쓰는 모든 컴포넌트가 매번 리렌더링되는 문제가 발생하게 된다!!!) value 객체를 사용해 Provider로 데이터를 제공하면 감싸진 자식 컴포넌트들은 해당 value 값에 바로 접근이 가능해진다.

// GrandParent
const GrandParent = () => {
  const [loggedIn, setLoggedIn] = useState(false);
  const [loading, setLoading] = useState(false);
  const value = useMemo(() => ({ setLoggedIn, setLoading }), [setLoggedIn, setLoading]);
  return (
    <UserContext.Provider value={value}>
      <Parent />
      <div>{loggedIn ? 'logins' : 'logout'}</div>
      <div>{loading ? 'loading...' : 'unloading...'}</div>
    </UserContext.Provider>
  );
};
export default GrandParent;

3️⃣ useContext 훅을 사용하여 데이터 읽어오기

: 컴포넌트 최상단에서 React.useContext로 전체 context 객체를 내려받아 데이터를 읽어올 수 있다. Parent 컴포넌트에서 props로 데이터를 내려받지 않고도 자식 컴포넌트인 Children 컴포넌트 내부에서 데이터에 바로 접근이 가능하게 된다.

기본형 > const value = useContext(SomeContext)

// Parent 
import React from 'react';
import Children from './Children';

const Parent = () => {
  return <Children />;
};
export default Parent;
// Children
import React, { useContext } from 'react';
import { UserContext } from './GrandParent';

const Children = () => {
  const { setLoading, setLoggedIn } = useContext(UserContext);
  return (
    <>
      <button onClick={() => setLoading((prev) => !prev)}>로딩토글</button>
      <button onClick={() => setLoggedIn((prev) => !prev)}>로딩토글</button>
    </>
  );
};
export default Children; 

전체코드

import React, { createContext, useMemo, useState } from 'react';
import Parent from './Parent';

export const UserContext = createContext({
  setLoggedIn: () => {},
  setLoading: () => {},
});
const GrandParent = () => {
  const [loggedIn, setLoggedIn] = useState(false);
  const [loading, setLoading] = useState(false);
  const value = useMemo(() => ({ setLoggedIn, setLoading }), [setLoggedIn, setLoading]);
  return (
    <UserContext.Provider value={value}>
      <Parent />
      <div>{loggedIn ? 'logins' : 'logout'}</div>
      <div>{loading ? 'loading...' : 'unloading...'}</div>
    </UserContext.Provider>
  );
};
export default GrandParent;

🔸한계

  • 복잡한 상태 관리는 어려울 수 있다.

    • Provider에 제공한 value가 달라지면 useContext를 쓰고 있는 모든 컴포넌트가 리렌더링 된다. 위의 예시에서 value 객체 안에는 관리해야하는 데이터가 여러개 묶여있는데 그 중 하나라도 바뀌면 전체가 리렌더링되는 문제가 발생하게 된다.(value 객체로 묶여있기 때문)
  • 너무 많은 Context를 사용하면 재사용성이 떨어진다.



💡

post-custom-banner

0개의 댓글