[React] Context API 사용하기

27px·2022년 10월 19일
0

App컴포넌트에서 모든 데이터를 Provider 컴포넌트에 내려주면
Provider 컴포넌트는 그 데이터를 자식 컴포넌트에게 다이렉트로 전달해줄 수 있습니다.
Provider컴포넌트의 자식 컴포넌트들은 Context 하에 존재합니다.
(Redux의 스토어 Provider 같은 느낌)

  • Provider 컴포넌트 문맥 안에 존재하지 않는 컴포넌트는 당연히 해당 데이터와 문맥에 접근할 수 없습니다.

Context API를 사용하면 불필요한 Props drilling을 피할 수 있습니다.

Context 생성

const Mycontext = React.createContext(defaultValue);

Context Provider를 통한 데이터 공급

<MyContext.Provider value={전역으로 전달하고자 하는 값}>
{//이 context 안에 위치할 자식 컴포넌트들}
</MyContext.Provider>

App.js

  • context 컴포넌트를 React.createContext()를 통해 만들어준다.

    createContext를 통해 만든 컨텍스트에 접근하여 데이터를 꺼내와야 하기 때문에 export로 내보내줍시다.

//해당 context도 es모듈 시스템으로 내보내줘야 다른 곳에서 사용할 수 있기 때문에 export 해준다.

export const DiaryStateContext = React.createContext();


//중략, App 컴포넌트의 return 부에 만들어둔 context를 제공한다. 

return (
    <DiaryStateContext.Provider value={data}>
      <div className="App">
        <DiaryEditor onCreate={onCreate} />
        <div>전체 일기: {data.length}</div>
        <div>기분 좋은 일기 개수 : {goodCount}</div>
        <div>기분 안좋은 일기 개수: {badCount}</div>
        <div>기분 좋은 일기 퍼센트: {goodRatio}%</div>
        <DiaryList diaryList={data} onRemove={onRemove} onEdit={onEdit} />
      </div>
    </DiaryStateContext.Provider>
  );

DiaryList 에서 불러와서 사용한다면,

  • useContext 훅에 만들어둔 context컴포넌트를 인자로 전달해줍니다. App.js에 만들어둔 context 컴포넌트는 당연히 export 된 상태여야 하고 사용할 곳으로 import 해오는 것을 명심하세용

src/components/DiaryList.js

import React, { useContext } from "react";
import DiaryItem from "./DiaryItem";
import { DiaryStateContext } from "../App";

function DiaryList({ onRemove, onEdit }) {
  // useContext의 인자로 만들어둔 컨텍스트 컴포넌트를 넣어줘야 한다.
  // export해놓은 context 컴포넌트를 임포트해오고 사용
  const diaryList = useContext(DiaryStateContext);

  return (
    <div className="DiaryList">
      <h2>일기 리스트</h2>
      <b>{diaryList.length}개의 일기가 있습니다.</b>
      <div>
        <ul>
          {diaryList.map((item) => {
            return (
              <DiaryItem
                key={item.id}
                {...item}
                onRemove={onRemove}
                onEdit={onEdit}
              />
            );
          })}
        </ul>
      </div>
    </div>
  );
}

React Development tool로 확인해보면 아래와 같은 결과를 확인할 수 있습니다.

export default vs export ?

  • export default 와 export의 차이점은 export default는 해당 파일에서 하나만 지정이 가능하며 별칭을 통해 다른 곳에서도 별칭을 지정하여 사용이 가능합니다. export로 내보내질 경우 {내보내는 애} 와 같은 형식으로 비구조화 할당으로 뽑아와 사용하며 별칭을 지정하여 사용할 수 없습니다. 대신 export로 내보내는 것에 개수 제한이 없습니다.

우리가 props로 넘겨준 것들 중 이제 값이 아닌 함수들이 남았는데요. 얘네는 또 다른 context를 만들어줘야 합니다.
이유는 DiaryStateContext.Provider도 컴포넌트이기 때문에 value로 넘겨주는 값을 여러 개 넣어주게 되면 이 value값이 변할때마다 해당 Provider도 렌더링이 일어나고 하위 컴포넌트들도 리렌더링이 일어나게 되기 때문입니다.

App.js

export const DiaryDispatchContext = React.createContext();

useMemo를 사용하여 렌더링이 일어나지 않도록 만들어둔 함수명들을 하나로 묶어 줍니다. 새로운 context에 value값으로 넘겨줘야 하기 때문이죵(비구조화 할당을 통해 뽑아쓸거니까 {함수명, 함수명, 함수명}) useMemo를 사용하지 않으면 애써 각각의 함수에 useCallback으로 해놨던 최적화가 의미없게 됩니다.

App.js

//useMemo를 사용하지않고 그냥 객체로 묶어주게되면 컴포넌트가 재생성될 때 같이 다시 만들어지게 된다.
  //그렇기 때문에 useMemo를 통해 묶어줘야 한다.
  const memoizedDispatches = useMemo(() => {
    return { onCreate, onRemove, onEdit };
  }, []);

onCreate, onEdit, onRemove 함수를 하나로 묶어서 그 데이터를 value값으로 넘겨줍니다.
결론적으로 Provider가 중첩된 모습으로 아래와 같이 나타나게 됩니다.

return (
    <DiaryStateContext.Provider value={data}>
      <DiaryDispatchContext.Provider value={memoizedDispatches}>
        <div className="App">
          <DiaryEditor />
          <div>전체 일기: {data.length}</div>
          <div>기분 좋은 일기 개수 : {goodCount}</div>
          <div>기분 안좋은 일기 개수: {badCount}</div>
          <div>기분 좋은 일기 퍼센트: {goodRatio}%</div>
          <DiaryList />
        </div>
      </DiaryDispatchContext.Provider>
    </DiaryStateContext.Provider>
  );

제대로 작동하는 지 확인하려면 리액트 디벨롭먼트 툴을 사용하면 됩니다.

  • 해당 Provider에서 3개의 함수 onCreate, onEdit, onRemove가 value로 잘 내려가고 있네요.🤸‍♀️

해당 값을 받아와 쓸 곳에서 Provider컴포가 제공하는 value를 가져와 봅시다.

src/components/DiaryEditor.js

import React, { useState, useRef, useEffect, useContext } from "react";
import { DiaryDispatchContext } from "./App";

const { onCreate } = useContext(DiaryDispatchContext);

onRemove와 onEdit을 prop으로 받던 DiaryItem에서도 context를 통해 value를 가져와봅시다.

src/components/DiaryItem.js

import React, { useState, useRef, useEffect, useContext } from "react";
import { DiaryDispatchContext } from "../App";

function DiaryItem({ id, author, contents, emotion, created_date }) {
  const { onRemove, onEdit } = useContext(DiaryDispatchContext);
  
  //코드 중략 

함수를 전달해주는 중간다리 역할을 하던 컴포넌트에서 prop으로 넘겨주는 부분을 지워주면
프롭 드릴링없이 context를 통해 다이렉트로 state를 value로 받아 사용할 수 있었습니다!

😎 context를 한 파일에서 생성할 때 개수 제한은 없지만 컴포넌트 간 공유해야하는 데이터가 많을 경우는 redux와 같은 상태관리 라이브러리를 사용하는 것이 더 나을 거 같습니다.

profile
안녕하세요?

0개의 댓글