일반적으로 React에서는 데이터는 부모로부터 props를 통해 전달된다.
그러나 컴포넌트의 깊이가 깊어지면 props-drilling 현상이 나타나기도 한다.

props-drilling은 props를 오로지 하위 컴포넌트로 전달하는 용도로만 쓰이는 컴포넌트들을 거치면서 React Component 트리의 한 부분에서 다른 부분으로 데이터를 전달하는 과정이다.
→ 이러한 이유로 전역 상태 관리를 사용합니다 (ex. recoil, context api)

컴포넌트의 상태는 공통된 상위 요소까지 끌어올려야만 공유될 수 있으며,
이 과정에서 거대한 트리가 다시 렌더링되는 효과를 야기하기도 한다.
예제를 보자.
import { createContext, useState } from "react";
import ContextChild1 from "./context/ContextChild1";
import ContextChild2 from "./context/ContextChild2";
import ContextChild3 from "./context/ContextChild3";
import RecoilChild1 from "./recoil/RecoilChild1";
import RecoilChild2 from "./recoil/RecoilChild2";
import RecoilChild3 from "./recoil/RecoilChild3";
export const ColorContext = createContext({
color: "red",
setColor: () => {},
state:''
});
function App() {
const [color, setColor] = useState("red");
const colorValue = { color, setColor, state: "state value" };
return (
<>
<ColorContext.Provider value={colorValue}>
<ContextChild1 />
<ContextChild2 />
<ContextChild3 />
</ColorContext.Provider>
</>
);
}
export default App;

ContextChild3 컴포넌트는 Context를 사용하지 않는다.
단지 Provider의 자식 컴포넌트라는 이유만으로 리렌더링되는 것이다.
이를 해결하기 위해서는 ContextChild3가 리렌더링 할 필요가 없다는 것을 알려줘야 한다.
React.memo 메소드로 컴포넌트를 매핑해준다.
export default memo(ContextChild3);

Context를 사용하지 않는 ContextChild3는 리렌더링이 되지 않는 것을 볼 수 있다.
변경 값이 없을 경우 캐싱된 값을 재사용할 수 있게 하는 기법
Context는 단일 값만 저장할 수 있으며, 자체 소비자(consumer)를 가지는 여러 값들의 집합을 담을 수는 없다.
여기서 자체 소비자(consumer)라는 개념은 Provider가 제공하는 상태를 소비하는 주체이다.
예제를 보자.
export const SomeContext = createContext({
color: "red",
setColor: () => {},
state: "",
});
위 Context는 color에 관한 값과 color와는 무관한 state라는 값을 제공하고자 한다.
위에서 말한 자체 소비자를 가진다는 것은
state라는 값만을 사용할 수 있는 주체가 있을 수 있는가라는 것이다.
import React, { memo, useContext, useEffect } from "react";
import { ColorContext, StateContext } from "../App";
const ContextChild2 = () => {
useEffect(() => {
console.log("ContextChild2 render!");
});
const { state } = useContext(SomeContext);
return <div>{state}</div>;
};
export default memo(ContextChild2);

하지만 ContextChild2는 SomeContext에서 state 값 만을 가져왔을 뿐이고,
state 값 만을 소비하는 주체가 아니다.
만약 state 값 만을 소비했다면 state값의 변화는 없었으니 리렌더링 되지 말았어야 했다.
이를 해결하기 위해서는 state값만을 제공하는 Provider를 별도로 생성해야 한다.
//App.js
import { createContext, useMemo, useState } from "react";
import ContextChild1 from "./context/ContextChild1";
import ContextChild2 from "./context/ContextChild2";
import ContextChild3 from "./context/ContextChild3";
export const ColorContext = createContext({
color: "red",
setColor: () => {},
});
export const StateContext = createContext("");
function App() {
const [color, setColor] = useState("red");
const colorValue = { color, setColor };
return (
<>
<ColorContext.Provider value={colorValue}>
<StateContext.Provider value="state value">
<ContextChild1 />
<ContextChild2 />
<ContextChild3 />
</StateContext.Provider>
</ColorContext.Provider>
</>
);
}
export default App;
//ContextChild2
import React, { memo, useContext, useEffect } from "react";
import { StateContext } from "../App";
const ContextChild2 = () => {
useEffect(() => {
console.log("ContextChild2 render!");
});
const { state } = useContext(StateContext);
return <div>{state}</div>;
};
export default memo(ContextChild2);

Context를 분리해서 제공함으로써 불필요한 리렌더링을 막을 수 있었다.
위와 같이 Context를 사용하면 렌더링 최적화가 까다로운 경우가 많다.
Recoil은 위 문제들을 쉽게 해결해준다.
Context의 Provider 역할을 하는 루트 컴포넌트로 App 컴포넌트를 감싸준다.
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import { RecoilRoot } from "recoil";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<RecoilRoot>
<App />
</RecoilRoot>
);
Atom은 상태의 일부이다.
Atom 값을 구독함으로써 상태를 읽고 수정할 수 있다.
import { atom } from "recoil";
export const colorAtom = atom({
key: "colorAtomKey",
default: "red",
});
import { atom } from "recoil";
export const stateAtom = atom({
key: "stateAtomKey",
default: "staet value",
});
import React, { useEffect } from "react";
import { useRecoilValue } from "recoil";
import { stateAtom } from "../atom/stateAtom";
const RecoilChild2 = () => {
useEffect(() => {
console.log("RecoilChild2 render!");
});
const state = useRecoilValue(stateAtom);
return (
<>
<div>{state}</div>
</>
);
};
export default RecoilChild2;
import React, { useEffect } from "react";
import { useRecoilState } from "recoil";
import { colorAtom } from "../atom/colorAtom";
const RecoilChild1 = () => {
useEffect(() => {
console.log("RecoilChild1 render!");
});
const [color, setColor] = useRecoilState(colorAtom);
return (
<>
<div style={{ color }}>{color}</div>
<button onClick={() => setColor("blue")}>change color to blue</button>
<button onClick={() => setColor("red")}>change color to red</button>
</>
);
};
export default RecoilChild1;
먼저 확인해 볼 것은
color 값을 수정했을 때 RecoilChild3가 리렌더링 되는가?
이다.

색을 변경 시켜도 colorAtom값을 구독하는 RecoilChild1만 리렌더링 되고 있다.
이는
여러 자체 소비자(Consumer)를 가지는가?
에 대한 의문도 해결해 준다.
자체 소비자를 가지지 않는다면
색을 바꿨을 때 stateAtom을 구독하는 RecoilChild2 또한 리렌더링 됐어야 한다.
하지만 각각 자체적인 소비자를 가지기 때문에 서로 영향을 주지 않는 것이다.
https://developer.themoviedb.org/reference/movie-top-rated-list