context는 리액트 컴포넌트간에 어떠한 값을 공유하게 해주는 기능이다.
주로 context는 전역으로 필요한 값을 다룰 때 사용하는데 , 하지만 꼭 전역적일 필요는 없다.
context는 단순히 리액트 컴포넌트에서 props가 아닌 다른 방식으로컴포넌트간에
값을 전달하는 방식이다.
props로 데이터를 전달하게 되면 발생하는 문제
리액트는 일반적으로 props를 통해 컴포넌트간에 값을 전달한다.
하지만 깊숙히 위치한 컴포넌트에게 데이터를 전달하는 경우 여러 컴포넌트를 거쳐 전달하기 때문에
가독성이 떨어지고 실수 할 가능성이 높아진다.
이러한 문제를 context로 해결할 수 있다.
context는 리액트 패키지에서 createContext라는 함수를 불러와 사용할 수 있다.
<script>
import { createContext } from 'react';
const MyContext = createContext();
</script>
Context 객체 안에는 provider라는 컴포넌트가 들어있다. 그리고 그 컴포넌트간에 공유하고자 하는 값을 value 라는 Props로 설정하면 자식 컴포넌트들에서 해당 값에 바로 접근을 할 수 있다.
<script>
function App() {
return (
<MyContext.Provider value="Hello World">
<GrandParent />
</MyContext.Provider>
);
}
</script>
이렇게 하면, 원하는 컴포넌트에서 useContext 라는 Hook을 사용하여 Context에 넣은 값에 바로 접근할 수 있다. 해당 Hook의 인자에는 createContext로 만든 값을 넣어준다.
<script>
import { createContext, useContext } from 'react';
function Message() {
const value = useContext(MyContext);
return <div>Received: {value}</div>;
}
</script>
provider의 vlaue로 state값과 함수를 묶은 객체
context의 value로 {data, onCreate, onRemove, onEdit}처럼 함수와 data를 함께 묶은 '객체'를 전달하게 되면 data의 값이 변경될 때 이 '객체' 자체가 다시 생성되게 된다.
이에 따라 함수는 재 생성되지 않지만 context에게 전달되는 value가 재 생성되기 때문에 리렌더가 발생하게 된다.
그렇기 때문에 하나의 context를 더 만들어 분리하여 전달한다.
useCallback으로 최적화 한 함수를 context로 전달 해줄 때
<script>
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", targetId });
}, []);
const onEdit = useCallback((targetId, newContent) => {
dispatch({ type: "EDIT", targetId, newContent });
}, []);
</script>
useMemo로 묶어서 보내주기.
<script>
const memoizedDispatches = useMemo(() => {
return { onCreate, onRemove, onEdit };
});
</script>
useMemo로 묶는 이유는 객체를 재 생성하지 않기 위함이다.
context에게 value로 전달하는 객체에 담기는 함수들이 useCallback으로 재 생성되지 않도록 만들어졌다고 해도 data State가 변하는 순간 App 컴포넌트가 리렌더되어 context에게 전달하는 객체 자체가 재 생성된다.
따라서 객체 자체도 재 생성하지 않게 하기 위해 useMemo를 사용한다.