[React] Recoil

찐새·2022년 8월 17일
0

React

목록 보기
8/21
post-thumbnail

State Management

  • 상위 컴포넌트에서 할당된 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 관리는 매우 비효율적이다.

Recoil

  • props drilling을 방지하고 state를 전역에서 다루기 위해 등장한 라이브러리.
  • 앱 어딘가 분리된 장소에 상태를 가진 recoil atom을 보관해두고 필요한 컴포넌트에서 직접 호출하는 방식

install

  • npm install recoil

usage

  • AppRecoilRoot로 감싼다.
// 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을 담을 파일 생성한다.
  • keydefaultatom를 저장한다.
// 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;
  • useStateprops drilling 없이 필요한 컴포넌트에서만 호출해 테마를 변경할 수 있다.

Set Atom Value

  • useSetRecoilState을 통해 호출한 atomdefault 값을 수정할 수 있다.
// 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

selector

  • atomstate를 가져다가 변형시켜 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를 호출한 변수 darkSelectorisDarkAtomstate를 받아 반대로 변경해 리턴한다.
// 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의 값을 확인하면, isDarkAtomtrue인 반면, selector를 거친 darkSelectorfalse를 반환한다.

참고
노마드 코더 - React JS 마스터클래스
Recoil

profile
프론트엔드 개발자가 되고 싶다

0개의 댓글