본문 바로가기
개발/리액트

useContext Hook

by ^..^v 2023. 2. 22.
728x90
반응형

다음 그림과 같이 부모 컴포넌트의 값 변경을 자식 컴포넌트로 전달해서 반영하는 경우, 부모 컴포넌트의 상태 변수를 자식 컴포넌트의 props 변수로 설정해서 전달합니다. 

import { useState } from "react";
import './App.css';

const GrandParent = () => {  
  const [grandParentSay, setGrandParentSay] = useState('');
  return (
    <>
      <legend>GrandParent</legend>
      <div className="box">
        <div className="parent">
          GrandParent say <input type="text" value={grandParentSay} onChange={e => setGrandParentSay(e.target.value)} />
        </div>
        <Parent grandParentSay={grandParentSay} />
      </div>
    </>
  );
};

const Parent = ({grandParentSay}) => {
  return (
    <>
      <legend>Parent</legend>
      <div className="box">
        <Child grandParentSay={grandParentSay} />
      </div>
    </>
  );
};

const Child = ({grandParentSay}) => {
  return (
    <>
      <legend>Child</legend>
      <div className="box">
        <GrandChild grandParentSay={grandParentSay} />
      </div>
    </>
  );
};

const GrandChild = ({grandParentSay}) => {
  return (
    <>
      <legend>GrandChild</legend>
      <div className="box">
        <div className="parent">GrandParent say "{grandParentSay}"</div>
      </div>
    </>
  );
};

function App() {
  return (
    <>
      <GrandParent />
    </>
  );
}

export default App;

 

GrandParent > Parent > Child > GrandChild 와 같이 여러 컴포넌트가 중첩되어 있으면 중간에 위치한 컴포넌트는 값 사용 여부와 관계 없이 부모 컴포넌트로부터 전달받은 값을 자식 컴포넌트로 다시 전달하는 코드를 포함해야 하므로 불필요한 코드 중복이 발생하게 되고 전달해야 하는 props 변수가 많을 경우 코드 복잡도가 증가하게 됩니다. 

 

다음은 부모 컴포넌트의 값은 자식 컴포넌트로, 자식 컴포넌트의 값은 부모 컴포넌트로 전달해서 출력하는 예제로, 부모 컴포넌트의 상태 변수 값을 전달하는 grandParentSay와 자식 컴포넌트의 상태 변수 값과 설정 함수인 grandChildSay, setGrandChildSay를 Parent, Child, GrandChild 컴포넌트의 props 변수로 연속해서 전달하는 것을 볼 수 있습니다. 

import { useState } from "react";
import './App.css';

const GrandParent = () => {  
  const [grandParentSay, setGrandParentSay] = useState('');
  const [grandChildSay, setGrandChildSay] = useState('');
  return (
    <>
      <legend>GrandParent</legend>
      <div className="box">
        <div className="parent">
          GrandParent say <input type="text" value={grandParentSay} onChange={e => setGrandParentSay(e.target.value)} />
        </div>
        <div className="child">
          GrandChild say "{grandChildSay}"
        </div>
        <Parent grandParentSay={grandParentSay} grandChildSay={grandChildSay} setGrandChildSay={setGrandChildSay} />      
      </div>
    </>
  );
};

const Parent = ({grandParentSay, grandChildSay, setGrandChildSay}) => {
  return (
    <>
      <legend>Parent</legend>
      <div className="box">
        <Child grandParentSay={grandParentSay} grandChildSay={grandChildSay} setGrandChildSay={setGrandChildSay} />
      </div>
    </>
  );
};

const Child = ({grandParentSay, grandChildSay, setGrandChildSay}) => {
  return (
    <>
      <legend>Child</legend>
      <div className="box">
        <GrandChild grandParentSay={grandParentSay} grandChildSay={grandChildSay} setGrandChildSay={setGrandChildSay} />
      </div>
    </>
  );
};

const GrandChild = ({grandParentSay, grandChildSay, setGrandChildSay}) => {
  return (
    <>
      <legend>GrandChild</legend>
      <div className="box">
        <div className="parent">GrandParent say "{grandParentSay}"</div>
        <div className="child">
          GrandChild say <input type="text" value={grandChildSay} onChange={e => setGrandChildSay(e.target.value)} />
        </div>
      </div>
    </>
  );
};

function App() {
  return (
    <>
      <GrandParent />
    </>
  );
}

export default App;

 

해당 코드를 context API를 이용해 다음과 같이 개선할 수 있습니다.

먼저 createContext를 임포트하고, 공유하길 원하는 데이터의 초기값을 인자로 넣어 컨텍스트 변수(여기에서는 MyContext)를 생성합니다. 

import { createContext, useContext, useMemo, useState } from "react";

const MyContext = createContext({
  grandParentSay: '', 
  grandChildSay: '', 
  setGrandChildSay: () => {}
});

 

다음으로 컨텍스트 공급자(Context Provider)를 통해 제공할 값을 정의합니다. 여러 개의 값을 공유할 경우 객체 형식으로 정의하며, useMemo 훅으로 객체를 캐싱해 불필요한 렌더링이 발생하지 않도록 합니다. 

 

const value = useMemo(() => ({grandParentSay, grandChildSay, setGrandChildSay}), [grandParentSay, grandChildSay, setGrandChildSay]);

 

값을 공유할 자식 컴포넌트를 MyContext.Provider로 감싸고 공유할 값을 정의한 변수를 value props의 값으로 전달합니다. 

<MyContext.Provider value={value}>
    <Parent />      
</MyContext.Provider>

 

이러면 중간에 위치한 컴포넌트(Parent, Child)에서 아무런 설정을하지 않아도 부모 컴포넌트가 설정한 컨텍스트 내의 값을 필요로 하는 자식 컴포넌트는 useContext 훅 함수를 이용해 가져와 사용할 수 있게 됩니다. 

const GrandChild = () => {
  const {grandParentSay, grandChildSay, setGrandChildSay} = useContext(MyContext);

  return (
    <>
      <legend>GrandChild</legend>
      <div className="box">
        <div className="parent">GrandParent say "{grandParentSay}"</div>
        <div className="child">
          GrandChild say <input type="text" value={grandChildSay} onChange={e => setGrandChildSay(e.target.value)} />
        </div>
      </div>
    </>
  );
};

 

전체 코드를 보면 중간에 위치한 컴포넌트(Parent, Child)에서 props 변수를 받고 전달하는 코드가 사라져 코드가 확연히 단순해진 것을 볼 수 있습니다.

import { createContext, useContext, useMemo, useState } from "react";
import './App.css';

const MyContext = createContext({
  grandParentSay: '', 
  grandChildSay: '', 
  setGrandChildSay: () => {}
});

const GrandParent = () => {  
  const [grandParentSay, setGrandParentSay] = useState('');
  const [grandChildSay, setGrandChildSay] = useState('');
  const value = useMemo(() => ({grandParentSay, grandChildSay, setGrandChildSay}), [grandParentSay, grandChildSay, setGrandChildSay]);
  return (
    <>
      <legend>GrandParent</legend>
      <div className="box">
        <div className="parent">
          GrandParent say <input type="text" value={grandParentSay} onChange={e => setGrandParentSay(e.target.value)} />
        </div>
        <div className="child">
          GrandChild say "{grandChildSay}"
        </div>

        <MyContext.Provider value={value}>
          <Parent />      
        </MyContext.Provider>
      </div>
    </>
  );
};

const Parent = () => {
  return (
    <>
      <legend>Parent</legend>
      <div className="box">
        <Child />
      </div>
    </>
  );
};

const Child = () => {
  return (
    <>
      <legend>Child</legend>
      <div className="box">
        <GrandChild />
      </div>
    </>
  );
};

const GrandChild = () => {
  const {grandParentSay, grandChildSay, setGrandChildSay} = useContext(MyContext);

  return (
    <>
      <legend>GrandChild</legend>
      <div className="box">
        <div className="parent">GrandParent say "{grandParentSay}"</div>
        <div className="child">
          GrandChild say <input type="text" value={grandChildSay} onChange={e => setGrandChildSay(e.target.value)} />
        </div>
      </div>
    </>
  );
};

function App() {
  return (
    <>
      <GrandParent />
    </>
  );
}

export default App;
728x90
반응형

댓글