Blog/React.js

[일기장 만들기_15] 컴포넌트 트리에 데이터 공급 (Context)

용디 2022. 3. 5. 00:00

🤷‍♀️ 프롭스 드릴링

단방향 흐름인 리액트를 사용하는데 발생한 문제점. 코드작성과 수정에 상당한 악영향을 끼침


🤷‍♀️ 어떻게 처리할 것인가?

모든데이터는 자식인 프로바이터(공급자 컨포넌트)에게 모든 데이터를 준다.

프로바이터는 자신의 자손에게 직통으로 데이터 줄수있다(프롭스 드릴링 사라짐) => 깔끔, 가독성 좋아짐

 

모든 데이터에 접근할 수 있는 컴포넌트들의 영역을 Context(문맥=글의방향)라고 표현 한다.


🤷‍♀️ 이용방법

Context 생성

const MyContext = React.createContext(defaultValue);

 

Context Provider를 통한 데이터 공급

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

Provider 컴포넌트는 value 프롭스를 받아 안에 있는 자식 컴포넌트에게 전달하는 기능을한다.

전달하는 프롭스의 개수 제한은 없으며, Provider 컴포넌트의 자식으로 존재하기만 하면 모든 컴포넌트는 전달 값을 받을 수 있다.


👾 프롭스 드릴링 제거

1. App 컴포넌트 밖에 일기 state를 전역 적으로 공급할 수 있도록 도와줄 컨텍스트를 만든다.

const DiaryStateContext = React.createContext();

 

2. 다른 컴포넌트가 접근 할 수 있도록 React.createContext 내보내주기

export const DiaryStateContext = React.createContext();

export default가 아닌 이유는 파일하나당 한개밖에 못쓰기 때문에

 

3. 공급자 컴포넌트로 랩핑

  return (
    <DiaryStateContext.Provider>
      <div className="App">
        <DiaryEditor onCreate={onCreate} />
        <div>전체일기 : {data.length}</div>
        <div>기분 좋은 일기 개수 : {goodCount}</div>
        <div>기분 나쁜 일기 개수 : {badCount}</div>
        <div>기분 좋은 일기 비율 : {goodRatio}</div>
        <DiaryList onEdit={onEdit} onRemove={onRemove} diaryList={data} />
      </div>
    </DiaryStateContext.Provider>
  );

 

4. 확인

 

5. 데이터 공급하기

value로 넘겨준 데이터값이 확인되는걸 볼 수 있다.

 

6. 자식 컴포넌트에서 데이터 사용하기

/* DiaryList */
import { useContext } from "react/cjs/react.development"; // 👉 import 확인
import { DiaryStateContext } from "./App"; // 👉
import DiaryItem from "./DiaryItem";

const DiaryList = ({onEdit, onRemove}) => { // 👉 받아오던 diaryList 지우고

    const diaryList = useContext(DiaryStateContext); // 👉 값을 꺼내고 싶은 Context 추가
    ~~~~~

 

6-1. 결과

자식 컴포넌트에서도 데이터가 넘어간걸 확인할 수 있다.

 

7. 프로바이터로 처리 했기 때문에 더이상 개별적으로 전달해 줄 필요가 없다.

App.js

(하지만 사실 얘는 드릴링이 안일어나는 부분이다.)

 

8. 상태변화를 주도하는 함수들 마저도 컨텍스트를 통해 공급

 

🤔 그럼 나머지도 data에 넣어주면 되는가? > 절대 안도ㅑㅏㅏㅏㅏㅏ!

이유는 Provider도 결국은 컴포넌트 이기 때문 > Provider도 컴포넌트이기 때문에 프롭이 바뀌어버리면 재생성 된다. (그럼 밑 컴포넌트도 재생성되어버리기 때문)

 

요약하자면 data와 함께 생성,수정,삭제 프롭을 같이 전달해 버리면 데이터 state가 바뀔때마다 리랜더링이 되어 버려서 결론적으로 지금까지 계획한 최적화가 풀려버림 

 

🤷‍♀️ 그럼 어떻게하지?

문맥을 중첩으로 이용하면됨 > DiaryStateContext는 오직 data의 state만을 공급하기 위해서 존재하도록 해주고, 나머지 onCreate, onRemove, onEdit와 같은 state를 변화시키는 함수들은 새로운 콘텍스트를 생성해서 내보내준다.

 

9. 별도로 도와줄 컨텍스트를 만든다.

export const DiaryDispatchContext = React.createContext();

 

10. 자식요소로 공급자 컴포넌트 추가 생성

중첩된 걸 확인할 수 있다.

 

11. useMemo를 사용하여 onCreate, onRemove, onEdit 하나의 값으로 묶어서 전달

  const memoizedDispatches = useMemo(() => {
    return {onCreate, onRemove, onEdit}
  }, []); // 👉 재생성되지 않게 빈배열로 전달
  
  ~~~~~
  
    return (
    <DiaryStateContext.Provider value={data}>
      <DiaryDispatchContext.Provider value={memoizedDispatches}>
        <div className="App">
          <DiaryEditor onCreate={onCreate} />
          <div>전체일기 : {data.length}</div>
          <div>기분 좋은 일기 개수 : {goodCount}</div>
          <div>기분 나쁜 일기 개수 : {badCount}</div>
          <div>기분 좋은 일기 비율 : {goodRatio}</div>
          <DiaryList onEdit={onEdit} onRemove={onRemove} />
        </div>
      </DiaryDispatchContext.Provider>
    </DiaryStateContext.Provider>
  );

 

✋ useMemo 사용하는 이유?

const dispatches = {
	onCreate, onRemove, onEdit
}

useMemo사용하지 않고 위처럼 한다면 앱 컴포넌트가 재생성이 될때 dispatches 객체도 다시 재생성 되기 때문에 어쩔수없이 useMemo를 사용해 재생성 되지않게(최적화가 풀리지 않게) 객체를 묶어줘야한다.

 

12. DiaryDispatchContext.Provider에서 넘겨주고 있으니 기존의 전달값은 지워주자

 

13. DiaryList에 넘어오는 값들도 지워주고, 프롭스 드릴링도 지워주자

 

14. 넘겨주는 값이 없으니 자식인 DiaryItem의 프롭도 수정해주자

 

15. 리액트로 심플 일기장 만들기 및 최적화 완성!


참고 : winterlood - codesandbox

 

🐱 GitHub : https://github.com/yeooji/React_simpleDiary

 

GitHub - yeooji/React_simpleDiary: React를 이용한 심플 일기장을 구현합니다.

React를 이용한 심플 일기장을 구현합니다. Contribute to yeooji/React_simpleDiary development by creating an account on GitHub.

github.com