상위 컴포넌트에서 할당된 state
를 하위 컴포넌트에서 사용하려면 props drilling
하여 전달해야 한다.
하위 컴포넌트의 개수에 따라 그 개수는 적을 수도, 많을 수도 있다.
컴포넌트 계층 구조가 App => Router => Posts => Post / Reply
로 되어있고, dark mode
여부를 전달한다고 치자.
function App() {
const [isDark, setIsDark] = useState(false);
const toggleMode = () => setIsDark((prev) => !prev);
return (
<>
<Router isDark={isDark} />
<button onClick={toggleMode}>Toggle Mode</button>
</>
);
}
function Router({ isDark }) {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Posts isDark={isDark} />} />
</Routes>
</BrowserRouter>
);
}
function Posts({ isDark }) {
return (
<div>
<Post isDark={isDark} />
</div>
);
}
function Post({ isDark }) {
return (
<div style={{ backgroundColor: isDark ? "black" : "white" }}>
Here is Post Component
</div>
);
}
function Reply({ isDark }) {
return <div>{isDark ? "now Dark Mode" : "now Light Mode"}</div>;
}
ts
라면 일일이 타입을 명시해야 한다.global state
관리는 매우 비효율적이다.props drilling
을 방지하고 state
를 전역에서 다루기 위해 등장한 라이브러리.recoil atom
을 보관해두고 필요한 컴포넌트에서 직접 호출하는 방식npm install recoil
App
을 RecoilRoot
로 감싼다.// index.tsx
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(
<React.StrictMode>
<RecoilRoot>
<App />
</RecoilRoot>
</React.StrictMode>
);
atom
을 담을 파일 생성한다.key
와 default
로 atom
를 저장한다.// atoms.ts
import { atom } from "recoil";
export const isDarkAtom = atom({
key: "isDark",
default: true,
});
atom
이 필요한 곳에서 값을 호출한다.// App.tsx
import { useRecoilValue } from "recoil";
import { isDarkAtom } from "./atoms";
import { darkTheme, lightTheme } from "./theme";
export default function App() {
const isDark = useRecoilValue(isDarkAtom);
return (
<React.Fragment>
<ThemeProvider theme={isDark ? darkTheme : lightTheme}>
<Posts />
</ThemeProvider>
</React.Fragment>
);
}
// Posts.tsx
function Posts() {
const isDark = useRecoilValue(isDarkAtom);
return (
<div style={{ backgroundColor: isDark ? "black" : "white" }}>
{posts.map((post) => (
<Post key={post.id}>
<Link to={`/${post.id}`}>{post.name}</Link>
</Post>
))}
</div>
);
}
export default Posts;
useState
나 props drilling
없이 필요한 컴포넌트에서만 호출해 테마를 변경할 수 있다.useSetRecoilState
을 통해 호출한 atom
의 default
값을 수정할 수 있다.// App.tsx
import { useRecoilValue, useSetRecoilState } from "recoil";
import { isDarkAtom } from "./atoms";
import { darkTheme, lightTheme } from "./theme";
export default function App() {
const isDark = useRecoilValue(isDarkAtom);
const setterFn = useSetRecoilState(isDarkAtom);
return (
<React.Fragment>
<ThemeProvider theme={isDark ? darkTheme : lightTheme}>
<Posts />
<button onClick={() => setterFn((prev) => !prev)}>모드 전환</button>
</ThemeProvider>
</React.Fragment>
);
}
value
와 변경 함수
모두 사용하고 싶다면 useState
처럼 useRecoilState
을 호출하여 사용한다.// App.tsx
import { useSetRecoilState } from "recoil";
import { isDarkAtom } from "./atoms";
import { darkTheme, lightTheme } from "./theme";
export default function App() {
const [isDark, setIsDark] = useRecoilState(isDarkAtom);
return (
<React.Fragment>
<ThemeProvider theme={isDark ? darkTheme : lightTheme}>
<Posts />
<button onClick={() => setIsDark((prev) => !prev)}>모드 전환</button>
</ThemeProvider>
</React.Fragment>
);
}
useRecoilValue
useSetRecoilState
useRecoilState
atom
의 state
를 가져다가 변형시켜 return
하는 기능이다.// atoms.ts
import { atom, selector } from "recoil";
export const isDarkAtom = atom({
key: "isDark",
default: true,
});
export const darkSelector = selector({
key: "darkSelector",
get: ({ get }) => {
const dark = get(isDarkAtom);
return !dark;
},
});
selector
를 호출한 변수 darkSelector
는 isDarkAtom
의 state
를 받아 반대로 변경해 리턴한다.// App.tsx
import { useRecoilValue } from "recoil";
import { darkSelector, isDarkAtom } from "./atoms";
export default function App() {
const atomOutput = useRecoilValue(isDarkAtom);
const selectorOutput = useRecoilValue(darkSelector);
console.log(`isDarkAtom : ${atomOutput}, darkSelector : ${selectorOutput}`);
// isDarkAtom : true, darkSelector : false
return (...);
}
useRecoilValue
로 가져온 selector
의 값을 확인하면, isDarkAtom
은 true
인 반면, selector
를 거친 darkSelector
는 false
를 반환한다.