자식 컴포넌트에 수많은 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>
);
};
전역상태관리 해주는 연습 많이 해야지!