일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- map
- SSR
- 타입 좁히기
- 투포인터
- recoil
- webpack
- 공변성
- TS
- ESlint
- async/await
- React
- Promise
- 결정 알고리즘
- app router
- dfs
- Cypress
- tailwind
- 호이스팅
- autosize
- 무한 스크롤
- useAppDispatch
- 리터럴 타입
- CI/CD
- 반공변성
- 인터섹션
- CORS
- 태그된 유니온
- Jest
- 이분 검색
- RTK Query
- Today
- Total
짧은코딩
컴포넌트 트리에 데이터 공급하기 본문
전달만하는 prop가 많이 생기면 이름 바꾸기도 어렵고 코드 작성, 수정에 악영향을 끼친다.
이런 상황을 props가 드릴처럼 땅을 파고 들어간다고 보고 Prop Drilling이라고 한다.
=> 부모에서 자식으로만 데이터를 전달하는 단방향 데이터 흐름이라서 이런 문제가 생긴다.
1. 모든 데이터를 Provider라는 공격자 역할을 하는 컴포넌트에게 전달
2. Provider는 특별해서 자신의 자손들에게 직통으로 데이터를 줄 수 있다.
=> Prop Diilling이 없어졌다.
3. 자식 컴포넌트들은 Provider에게 직통으로 데이터를 받는다.
따라서 Provider 아래에 존재하는 모든 컴포넌트를 문맥화에 존재한다.
Context
-예시
const ThemeContext = React.createContext('light');
class App extends React.Component {
render() {
// Provider를 이용해 하위 트리에 테마 값을 보내줍니다.
// 아무리 깊숙히 있어도, 모든 컴포넌트가 이 값을 읽을 수 있습니다.
// 아래 예시에서는 dark를 현재 선택된 테마 값으로 보내고 있습니다.
return (
<ThemeContext.Provider value="dark">
{/*이 Context안에 위치할 자식 컴포넌트들*/}
<Toolbar />
</ThemeContext.Provider>
);
}
}
context는 인자로 defaultValue를 가지고 만들어지고 Context Provider를 통해 공급이 가능하다.
- App.js
export const DiaryStateContext = React.createContext();
export default는 파일당 1개만 사용 가능해서 export만 한다. export하면 여러 개를 내보낼 수 있다.
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>
);
이렇게 App의 return의 최상위 태그를 바꿔준다.
이렇게 Context.Provider를 보면 파란색으로 표시되는 부분이 문맥의 부분이다.
따라서 파란색 부분들은 Context.Provider가 공급하는 모든 데이터를 어디서든 가져다 쓸 수 있다.
-데이터 공급
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 onEdit={onEdit} onRemove={onRemove} diaryList={data} />
</div>
</DiaryStateContext.Provider>
);
value로 데이터를 공급하면 된다.
-DiaryList.js(자식 컴포넌트가 데이터 사용하기)
import { useContext } from "react";
import { DiaryStateContext } from "./App";
import DiaryItem from "./DiaryItem";
const DiaryList = ({ onEdit, onRemove }) => {
const diaryList = useContext(DiaryStateContext);
return (
<div className="DiaryList">
<h2>일기 리스트</h2>
<h4>{diaryList.length}개의 일기가 있습니다.</h4>
<div>
{diaryList.map((it) => (
<DiaryItem key={it.id} {...it} onEdit={onEdit} onRemove={onRemove} />
))}
</div>
</div>
);
};
DiaryList.defaultProps = {
diaryList: [],
};
export default DiaryList;
diaryList를 이제 prop로 받을 필요없이 context에서 받아온다. 따라서 useContect를 사용하여 App.js에서 만든 DiaryStateContext를 받아온다.
그리고 보면 hooks의 Context에 일기 데이터들이 잘 전달된 것이 보인다.
다시 App.js에 돌아가서
<DiaryStateContext.Provider value={data}>
<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>
</DiaryStateContext.Provider>
DiaryList 컴포넌트에 diaryList를 prop로 전달하지 않아도 된다.
-상태 변화를 주도하는 함수도 Context로 공급하기
App.js에 DiaryStateContext.Provider의 value에 onCreate, onRemove, onEdit를 모두 넣어주면 될거같지만 이렇게하면 안된다. 왜냐하면 Provider도 컴포넌트라서이다. 그러면 Provider가 재생성되면 리렌더가 일어나고 결국 우리가 만들었던 최적화가 의미가 없어진다.
=> 이럴땐 Context를 중첩으로 사용하면 된다.
App.js
export const DiaryDispatchContext = React.createContext();
따라서 이렇게 Context를 하나 더 만든다.
return (
<DiaryStateContext.Provider value={data}>
<DiaryDispatchContext.Provider value={memoizedDispatch}>
<div className="App">
<DiaryEditor />
<div>전체 일기 : {data.length}</div>
<div>기분 좋은 일기 개수 : {goodCount}</div>
<div>기분 나쁜 일기 개수 : {badCount}</div>
<div>기분 좋은 일기 비율 : {goodRatio}</div>
<DiaryList />
</div>
</DiaryDispatchContext.Provider>
</DiaryStateContext.Provider>
);
그리고 중첩으로 이렇게 해준다.
그러면 onCreate, onRemove, onEdit은 재생성되지 않는 함수로만 이루어져있어서 재생성되지 않는다.
const memoizedDispatch = useMemo(() => {
return { onCreate, onRemove, onEdit };
}, []);
onCreate, onRemove, onEdit을 하나로 묶는다. 두번째 인자를 빈 배열로 한 이유는 절대 재생성되지 않게 하기 위해서이다.
useMemo를 활용하지 않으면 재생성되게된다.
<DiaryDispatchContext.Provider value={memoizedDispatch}>
그리고 DiaryDispatchContext.Provider의 value에 전달한다.
<DiaryEditor />과 <DiaryList />는 더 이상 onCreate, onRemove, onEdit이 함수들을 전달 받을 필요가 없어져서 제거한다.
-DiaryEditor.js
onCreate prop를 제거한다.
const { onCreate } = useContext(DiaryDispatchContext);
그리고 onCreate를 Context를 통해 비구조화 할당으로 가져온다.
-DiaryList.js
onRemove, onEdit를 제거한다.
그리고 Item에게 전달하는 Prop Drilling을 제거한다.
<DiaryItem key={it.id} {...it} />
-DiaryItem.js
onEdit, onRemove를 더 이상 prop로 받지 않게 한다.
const { onRemove, onEdit } = useContext(DiaryDispatchContext);
그리고 Context로 부터 받는다.
'인프런, 유데미 > 한입 크기로 잘라 먹는 리액트' 카테고리의 다른 글
페이지 라우팅 (0) | 2022.06.16 |
---|---|
일기장 만들기 되짚어보기 (0) | 2022.06.14 |
복잡한 상태 관리 로직 분리하기 (0) | 2022.05.30 |
최적화4-삭제, 수정 최적화 (0) | 2022.05.29 |
최적화3-컴포넌트 최적화 (0) | 2022.05.29 |