본문 바로가기
카테고리 없음

context Api 공부

by 띠리에이터 2024. 2. 2.

자식 컴포넌트에 수많은 props로 내려주면 가독성이 너무 안좋은 propsDrilling이 발생, 

-> 코드를 읽을 때 해당 prop를 추적하기 힘들어지고 유지보수가 어려워진다

-> react의 상태관리인 context를 사용해보자

 

컴포넌트 구조

현재 App컴포넌트에서 시작해 자식 컴포넌트로 4~5개의 prop을 내려주는 상황 DiaryList를 보면

import React from "react";
import DiaryItem from "./DiaryItem";
const DiaryList = ({ diaryList, onRemove, onModify }) => {
  return (
    <div>
      <h4>{diaryList.length}개의 일기가 있습니다. </h4>
      <div>
        {diaryList.map((diary) => {
          return (
            <DiaryItem
              key={diary.id}
              {...diary}
              onRemove={onRemove}
              onModify={onModify}
            />
          );
        })}
      </div>
    </div>
  );
};

export default DiaryList

이런 식으로 구조가 되어있다. DiaryList의 자식 컴포넌트인 DiaryItem 컴포넌트는 더 처참한 수준

const DiaryItem = ({
  author,
  content,
  emotion,
  createdDate,
  id,
  onRemove,
  onModify,
})

react.createContext로 새로운 Context 객체를 생성해주고 (컴포넌트 밖에서 선언해야함)

return문 안에 <DiaryStateContext.Provider>로 감싸주자

-> <DiaryStateContext.Provider>는 context를 사용하는 컴포넌트에 context의 값을 전달해주는 역할

그리고 <DiaryStateContext.Provider> 컴포넌트에 value={data} 데이터state를 공급해주자 

export const DiaryStateContext = React.createContext();

const App = () => {

... 코드 생략

return (
    <DiaryStateContext.Provider value={data}>
      <div className="divApp">
        <div className="diaryBox">
          <DiaryEditor onCreate={onCreate} />
          <div>전체 일기 : {data.length}</div>
          <div>기분 좋음 : {good}</div>
          <div>기분 나쁨 : {bad}</div>
          <div>기분 비율 : {goodRatio}</div>
          <DiaryList onRemove={onRemove} diaryList={data} onModify={onModify} />
        </div>
      </div>
    </DiaryStateContext.Provider>
  );
};

 

그리고 자식 컴포넌트에서는 context의 값을 꺼내주기 위해 useContext hook을 사용해야한다. (react hook은 컴포넌트 안에서 사용)

import React, { useContext } from "react";
import DiaryItem from "./DiaryItem";
import { DiaryStateContext } from "./App";
const DiaryList = ({ onRemove, onModify }) => {
  const diaryList = useContext(DiaryStateContext);
  return (
    <div>
      <h4>{diaryList.length}개의 일기가 있습니다. </h4>
      <div>
        {diaryList.map((diary) => {
          return (
            <DiaryItem
              key={diary.id}
              {...diary}
              onRemove={onRemove}
              onModify={onModify}
            />
          );
        })}
      </div>
    </div>
  );
};

App컴포넌트에서 export 한 DiaryStateContext를 import 하고 useContext 인자로는 context를 전달

DiaryList 컴포넌트에 context 값으로 data 가 잘 들어온 것을 확인할 수 있다. 

 

state를 변경시키는 함수들도 context로 전달해보자!

 

App컴포넌트에서 내려줬던 onRemove, onCreate, onModify 함수도 context로 내려줄 수 있다. (상단 설계도 참고)

 

export const DiaryDispatchContext = React.createContext();

 

함수를 담아줄 context 를 새로 만들어준다

-> DiaryStateContext.Provider 컴포넌트에 함수 prop를 똑같이 내려주게 되면 data state가 바뀔 때마다 리렌더링이 일어나게 되어서 기존에 만들었던 최적화가 다 풀려버림 그래서 중첩으로 따로 만들어준다

export const DiaryStateContext = React.createContext();
export const DiaryDispatchContext = React.createContext(); // 이게 새로 만든것

const App = () => {
..코드 생략
const onCreate = useCallback((author, content, emotion) => {
    dispatch({
      type: "CREATE",
      data: { author, content, emotion, id: dataId.current },
    });
    dataId.current += 1;
  }, []);

  const onRemove = useCallback((targetId) => {
    dispatch({ type: "REMOVE", data: targetId });
  }, []);

  const onModify = useCallback((targetId, newContent) => {
    dispatch({ type: "MODIFY", data: { targetId, newContent } });
  }, []);
  
  const memoizedDispatch = useMemo(() => { // 이 함수에 3개의 prop을 묶고 
   return {onCreate, onRemove, onModify};
  }, []); 
 // usememo를 사용한 이유는 앱 컴포넌트가 렌더링 될 때 객체가 리렌더링 되지 않게 한것
  return (
    <DiaryStateContext.Provider value={data}>
      <DiaryDispatchContext.Provider value={memoizedDispatch}> // 컴포넌트에 내려준다
        <div className="divApp">
          <div className="diaryBox">
            <DiaryEditor />
            <div>전체 일기 : {data.length}</div>
            <div>기분 좋음 : {good}</div>
            <div>기분 나쁨 : {bad}</div>
            <div>기분 비율 : {goodRatio}</div>
            <DiaryList />
          </div>
        </div>
      </DiaryDispatchContext.Provider>
    </DiaryStateContext.Provider>
  );
};

export default App;

 

  자식 컴포넌트인 DiaryList,  DiaryEditer 컴포넌트에 porps를 지워줘도 값이 잘 내려오는 걸 확인할 수 있다. 

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

const DiaryList = () => {
  const diaryList = useContext(DiaryStateContext);
  //비구조화 할당으로 값을 가져온다
  return (
    <div>
      <h4>{diaryList.length}개의 일기가 있습니다. </h4>
      <div>
        {diaryList.map((diary) => {
          return (
            <DiaryItem
              key={diary.id}
              {...diary}
            />
          );
        })}
      </div>
    </div>
  );
};

전역상태관리 해주는 연습 많이 해야지!