Props Drilling
해당 컴포넌트에 필요한 것은 아니지만 데이터를 내려주기 위해서 onRemove()나 onEdit() 같이 DiaryList 컴포넌트를 거쳐가는 props들이 있다. 이러한 props들이 많으면 이름이나 코드를 수정할 때 모든 걸 일일이 들어가서 수정해줘야 하는 어려움이 있고, 그렇기 때문에 코드가 복잡해질 경우 유지보수를 하기 어렵다는 단점이 있다. 이처럼 props가 땅 파고 계속 들어가는 것처럼 내려가는 모양이라 위 현상을 Props Drilling이라고 한다.
이를 방지하기 위해서 context를 사용한다.
Context – React
A JavaScript library for building user interfaces
ko.legacy.reactjs.org
부모 컴포넌트의 바로 아래에 모든 데이터를 받고 전역으로 관리하는 Provider 컴포넌트가 하위 컴포넌트에게 데이터를 주면 단계마다 일일이 Prop를 넘겨주지 않고도 데이터를 전달할 수 있다.
const MyContext = React.createContext(defaultValue);
<MyContext.Provider value={전역으로 전달하려는 데이터}>
// 이 문맥 안에 위치할 자식 컴포넌트들
</MyContext.Provider>
이렇게 Children을 감싸주는 형태로 사용하면 된다.
예시
export const DiaryStateContext = React.createContext();
export const DiaryDispatchContext = React.createContext();
먼저 위와 같이 Provider 컴포넌트를 생성해준다.
StateContext는 함수명대로 state를 관리할거고 DispatchContext는 dispatch를 관리할 것이다.
App 컴포넌트의 마지막 줄처럼 내보내줘야 하는데, Defalut는 하나만 쓸 수가 있다.
하지만 그 키워드를 빼고 그냥 export 하는 것은 제한이 없어 여러 개를 사용해도 된다.
useReducer에서 초기값으로 썼던 data가 지금의 데이터 값이다.
현재 더미데이터를 받아와서 렌더링 하면 20개의 일기가 보여진다.
Context를 생성해 최상단에 묶어 문맥을 만들고, 데이터를 내려줄 때는 value={}로 내려준다.
return (
<DiaryStateContext.Provider value={data}>
<DiaryDispatchContext.Provider value={memoizedDispatches}>
<div className="App">
<DiaryEditor />
<div> 전체 일기: {data.length} </div>
<div> 기분 좋은 일기 개수: {goodCount} </div>
<div> 기분 나쁜 일기 개수: {badCount} </div>
<div> 기분 좋은 일기 비율: {goodRation} </div>
<DiaryList />
</div>
</DiaryDispatchContext.Provider>
</DiaryStateContext.Provider>
);
컴포넌트를 감싼 후 확장 프로그램으로 확인해보면 이렇게 잘 래핑된 것을 볼 수 있다.
사실 이것보다는 onRemove, onEdit 함수의 리팩토링이 더 필요하다.
얘네야말로 실제로 쓰이지 않는데 거쳐가고 있다.
전에 onRemove가 아니라 onDelete였는데 함수명 바꾸는데도 번잡스러웠던 적이 있다.
대신 함수들은 그대로 value에 넣으면 안된다.
그렇게 넣으면 Provider 컴포넌트도 컴포넌트라 렌더링이 되는데, 그 때 그 아래에 있는 함수 컴포넌트들도 다 강제로 리렌더링이 되어 버린다. 그러면 이전까지 useMemo며 뭐며 최적화를 했던 것들이 말짱 도루묵이 되어 버린다.
이를 위해 기억해야 하는 함수 값들은 memoizedDispatches에 할당한다.
그리고 이걸 DiaryDispatchContext.Provider 컴포넌트에 value={memoizedDispatches}로 내려준다.
이제 이 밑에 있는 DiaryEditor, DiaryList 컴포넌트에서 구조 분해 할당으로 받아오면 된다.
const memoizedDispatches = useMemo(() => {
return { onCreate, onRemove, onEdit };
}, []);
그리고 DiaryItem의 경우 기존 코드에서 더 깔끔해졌다.
// 기존
const DiaryItem = ({
author,
content,
created_date,
emotion,
id,
onRemove,
onEdit,
}) => {
// 현재
const DiaryItem = ({ author, content, created_date, emotion, id }) => {
const { onEdit, onRemove } = useContext(DiaryDispatchContext);
구조 분해 할당으로 이렇게 가져오기만 하면 된다.
일단은 가볍게 이렇구나 하고 넘어갔다.
**참고
한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지 - 인프런 | 강의
개념부터 독특한 프로젝트까지 함께 다뤄보며 자바스크립트와 리액트를 이 강의로 한 번에 끝내요. 학습은 짧게, 응용은 길게 17시간 분량의 All-in-one 강의!, 리액트, 한 강의로 끝장낼 수 있어요.
www.inflearn.com
'⚛️ React > 💭 한 입 크기 React' 카테고리의 다른 글
[React] React-Router (0) | 2023.07.28 |
---|---|
[React] useReducer로 상태 변화 로직을 분리하기 (0) | 2023.07.26 |
[React] React.memo로 컴포넌트 재사용하기 (0) | 2023.07.26 |
[React] useMemo로 연산한 값 재사용하기 (0) | 2023.07.26 |
[React] 리액트의 생애주기 (Mount, Update, Unmount) (0) | 2023.07.25 |