Recoil 공식 문서를 참고한 글입니다.
1. Recoil 기본 개념
recoil이란 React를 위한 상태 관리 툴이다. recoil을 사용하려면, atoms에서 selectors를 거쳐 React 컴포넌트로 내려가는 data-flow graph를 만들 수 있다.
Atoms
atom은 상태 단위로, atom 값을 읽는 컴포넌트들은 암묵적으로 atom을 구독하기 때문에 atom에 어떤 변화가 생기면 자동적으로 재렌더링이 된다. 디버깅이나 지속성 등을 위해 고유한 key가 필요하고, React 컴포넌트 상태처럼 기본 값도 가진다.
const fontSizeState = atom({
key: 'fontSizeState',
default: 14,
});
atom을 읽고 쓸 때는 useRecoilState라는 훅을 사용한다.
function FontButton() {
const [fontSize, setFontSize] = useRecoilState(fontSizeState);
return (
<button onClick={() => setFontSize((size) => size + 1)} style={{fontSize}}>
Click to Enlarge
</button>
);
}
Selectors
atoms나 다른 selector로부터 입력을 받아들이는 순수 함수다. 상위의 atoms나 selectors가 업데이트되면 하위의 selector도 업데이트된다. atom에는 최소의 상태 집합만 저장하고 나머지는 seletor에서 처리한다. 쉽게 말하면, atom의 상태를 변형하거나 파생된 상태를 사용할 때 사용된다.
이때, get은 사용된 함수를 뜻한다. get 인자를 통해서 다른 atoms와 selectors에 접근할 수 있다.
const fontSizeLabelState = selector({
key: 'fontSizeLabelState',
get: ({get}) => {
const fontSize = get(fontSizeState);
const unit = 'px';
return `${fontSize}${unit}`;
},
});
selector는 useRecoilValue라는 훅을 사용해 읽을 수 있다. useRecoilValue는 useRecoilState와 달리 읽기만 가능하고 쓰기는 할 수 없다.
function FontButton() {
const [fontSize, setFontSize] = useRecoilState(fontSizeState);
const fontSizeLabel = useRecoilValue(fontSizeLabelState);
return (
<>
<div>Current font size: ${fontSizeLabel}</div>
<button onClick={setFontSize(fontSize + 1)} style={{fontSize}}>
Click to Enlarge
</button>
</>
);
}
2. Recoil 적용하기
RecoilRoot로 감싸기
App.tsx 전체를 부모 트리인 RecoilRoot로 감싸야 recoil을 전체 컴포넌트에서 사용할 수 있다.
// App.tsx
import { Router } from "./Router";
import { BrowserRouter } from "react-router-dom";
import { RecoilRoot } from "recoil";
function App() {
return (
<RecoilRoot>
<BrowserRouter>
<Router />
</BrowserRouter>
</RecoilRoot>
);
}
export default App;
atom 정의하기
전역 상태로 사용자가 선택한 언어 설정을 관리해야 했기 때문에 아래와 같이 설정했다. 기본 값을 ko로 설정하였다.
// atoms.tsx
import { atom } from "recoil";
const languageState = atom({
key: "languageState",
default: "ko",
});
export default languageState;
atom 사용하기
특정 페이지에서 사용자가 설정한 언어 정보를 서버로 넘겨야 했다. languageState를 import해서 request를 보내는데 language 변수를 사용했다.
// UploadPage.tsx
import languageState from "../context/atoms";
function UploadPage() {
const language = useRecoilValue(languageState);
console.log(language);
const quizRequest = {
title: quizTitle,
idList: selectedCardIds,
language: language,
};
return ...
}
atom 업데이트하기
MyPage에서 언어를 선택할 수 있기 때문에, useRecoilState를 통해 atom에 설정된 language 값을 불러오고, 새롭게 설정된 language 값을 업데이트했다.
import languageState from "../context/atoms";
import { useRecoilState } from "recoil";
function MyPage() {
const [language, setLanguage] = useRecoilState(languageState);
const onChangeLanguage = (lang: string) => {
i18n.changeLanguage(lang);
setDropdownOpen(false);
setLanguage(lang);
};
...
}
3. Recoil Hooks
useRecoilState
이 훅은 atom의 상태를 읽고 쓸 수 있다. React의 useState와 동일하게 사용한다.
import textState '../context/textState'
...
const [text, setText] = useRecoilState(textState);
useSetRecoilState
atom의 상태를 업데이트하는 setter 함수를 반환한다. 위의 language 업데이트 함수를 기존에는 아래와 같이 작성했었다. language를 useRecoilValue로 가져오고, useSetRecoilState로 업데이트하도록 읽고 쓰기를 분리했다. 이런식으로 작성해도 기능상엔 무방하다. 차이라고 하면 불필요한 리렌더링을 줄일 수 있다는 것이다.
import languageState from "../context/atoms";
import { useRecoilValue, useSetRecoilState } from "recoil";
function MyPage() {
const language = useRecoilValue(languageState);
const setLanguage = useSetRecoilState(languageState);
const onChangeLanguage = (lang: string) => {
i18n.changeLanguage(lang);
setDropdownOpen(false);
setLanguage(lang);
};
...
}
useRecoilValue
atom 상태를 읽기만 할 수 있다. setter 함수 없이 atom 값만 반환하는 훅이다.
const language = useRecoilValue(languageState);
4. Recoil 장점
- 단순한 data-flow: atom에서 selector로 흘러 들어가는 단방향 구조
- concurrency mode 지원: 비동기 selector를 만들어서 동시성 모드를 구현할 수 있
'Web > React' 카테고리의 다른 글
[React] useEffect 란? (feat.생명주기) (0) | 2024.07.13 |
---|---|
[React] Flux 패턴이란? (0) | 2024.07.08 |
[React] 리액트에서 상태 관리가 필요한 이유(feat.MVC) (0) | 2024.07.07 |
[React] 새로고침 시 값 유지하기(feat.localStorage) (0) | 2024.06.07 |
[React] Newline required at end of file but not found. (0) | 2022.06.06 |