React의 특징 중 하나는 데이터가 단방향으로 흐른다는 점이다. 즉, 부모 component에서 자식 component로 props가 옮겨오는 식의 형태를 띄고 있다. 따라서 모든 component에서 쓰여야 하는 상태(ex 유저 정보)가 있다면 이 정보를 계속 부모에서 자식으로, 또 그 자식으로 전달해야 하는 어려움이 있다.
❗React의 한계
- 상태 공유를 위해 상위 요소까지 끌어올리면 거대한 트리가 재렌더될 수도...
- Context는 단일 값만을 저장할 수 있고, 자체 Consumer를 가지는 여러 값의 집합은 담을 수 없다.
- 최상단(state가 존재하는 곳)부터 트리의 잎(state가 사용되는 곳)까지의 코드 분할이 어렵다.
따라서 Redux를 통해 상태관리를 시행했지만 React에 최적화되어있지 않아서 사용에 불편함이 있었다고 한다.
❗먼저, Redux와 Recoil에 대해서 비교해보자면
- 액션, 리듀서, 미들웨어 등 boilerplate 코드가 많이 발생하는 Redux 와는 대조적으로 Recoil 은 boilerplate-free API 제공한다. React 의 useState 처럼 간단한 게터(get) / 세터(set) 인터페이스로 사용 가능하다.
- Redux 의 상태 구조는 트리 구조를 따르지만 Recoil 은 방향 그래프(directed graph, digraph) 를 따른다.
- Recoil 은 상태를 사용하는 컴포넌트를 수정하지 않고 파생 데이터(derived data)를 대체할 수 있다.
- 기본적으로 아톰(atom)의 데이터가 변경되면 해당 atom 을 구독하는 모든 컴포넌트들은 갱신된다. 그러나 Redux 에서는 해당 기능을 수행하기 위해 reselect 같은 3rd-party 라이브러리가 필요하다.
Recoil을 사용하면 atoms (공유 상태)에서 selectors (순수 함수)를 거쳐 React 컴포넌트로 내려가는 data-flow graph를 만들 수 있다. Atoms는 컴포넌트가 구독할 수 있는 상태의 단위이며 Selectors는 atoms 상태값을 동기 또는 비동기 방식을 통해 변환한다.
Recoil 시작해보자!
Recoil 패키지를 설치해준다.
npm install recoil
📌 RecoilRoot
recoil을 사용하기 위해서는 사용하고자하는 부모 컴포넌트에다 <RecoilRoot>를 사용해야 한다.
보통 전역적으로 사용하기 때문에 루트 컴포넌트에 넣는게 좋다.
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { RecoilRoot } from 'recoil';
ReactDOM.render(
<React.StrictMode>
<RecoilRoot>
<App />
</RecoilRoot>
</React.StrictMode>,
document.getElementById('root')
);
📌 Atoms
atom은 redux의 store와 같은 개념이다.
데이터 상태의 단위이며, 업데이트와 구독이 가능하다.
atom의 값이 바뀌면 구독하고있는 장소는 모두 새로운 값으로 리렌더링된다.
여러개 생성이 가능하다.
atom은 key와 default값을 설정해줘야 한다.
import { atom } from "recoil";
export default atom({
key: 'countState',
default: 0,
});
- key
atom을 식별하는데 필요한 고유한 문자열
프로젝트 전체에서 다른 atom, selector에 대해 고유해야 한다.
- default
초기값을 설정해준다.
atom으로 정의한 상태는 아래의 Hook을 이용하여 사용할 수 있다.
const [recoilCounter, setRecoilCounter] = useRecoilState(recoilCounterState);
const recoilCounterValue = useRecoilValue(recoilCounterState);
const setRecoilCounter = useSetRecoilState(recoilCounterState);
const resetRecoilCounter = useResetRecoilState(recoilCounterState);
각각의 Hook에 대해 정리하면 다음과 같다.
- useRecoilState
atom의 상태를 구독.
useState Hook과 같이 배열의 첫번째 파라미터로 상태, 두번째 파라미터로 상태에 대한 setter 함수를 반환. - useRecoilValue
setter 함수 없이 atom의 상태만 반환. - useSetRecoilState
atom 상태 없이 setter 함수만 반환. - useResetRecoilState
atom 상태를 default 상태로 reset.
📌Selectors
Selectors는 atom에서는 불가능한 비동기 처리와 복잡한 로직을 구현할 수 있다.
공식문서에는 아래와 같이 정의되어 있습니다.
Selector는 파생된 상태(derived state)의 일부를 나타낸다.
파생된 상태를 어떤 방법으로든 주어진 상태를 수정하는 순수 함수에 전달된 상태의 결과물로 생각할 수 있다.
순수 함수란, 함수에 동일한 인자가 주어졌을 때 항상 같은 값을 리턴하는 함수입니다. (외부의 영향은 받지 않아야 합니다.)
import { atom } from "recoil";
export default atom({
key: 'countState',
default: 0,
});
import { DefaultValue, selector } from "recoil";
import countState from "../atom/countState";
export default selector({
key: "countSelector",
get: ({get}): number => {
const count = get(countState);
return count + 1;
},
set: ({set, get}, newCount)=>{
return set(countState, newCount + 10)
}
})
selector는 총 세 가지 값을 가진다.
- key
atom의 key와 동일하며 프로젝트 전체에서 고유한 문자열을 가져야 한다.
- get
파생된 상태를 반환하는 곳
get(countState) 처럼 countState를 get하고 있으면 countState가 바뀔 때마다 새로운 값을 리턴해준다.
get()을 여러번 사용 가능하고 그중 하나라도 변하게 되면 리렌더링된다.
get: ({get}): number => {
// countState를 구독하고 있습니다.
// countState가 바뀔 때마다 1증가 시켜서 반환합니다.
const count = get(countState);
return count + 1;
},
- set
set 없이 get만 제공되면 selector는 read-only 한 상태이지만 set을 제공하면 쓰기 가능한 상태를 반환한다.
set은 selector의 값을 수정하는 것이 아닌 수정 가능한 atom의 값을 바꿔준다.
set: ({set, get}, newCount)=>{
// 설명을 위한 코드로,
// 현재 count는 사용하고 있지 않습니다.
const count = get(countState);
return set(countState, newCount + 10)
}
set안에서의 get()은 구독하지 않으며 단순히 atom이나 selector의 값을 찾는데 사용한다.
set()은 두가지 매개변수를 받는다.
첫 번째는 Recoil의 상태 (위 코드에서는 countState)
두 번째는 어떤 값으로 바뀔 건지 즉 새로운 값을 넣는다.
만약 전달받은 새롭운 값이 업데이트 함수나 초기화 액션이라면 DefaulValue 일 수도 있다.
📌 비동기 Selector
const myQuery = selector({
key: 'MyQuery',
get: async ({get}) => {
return await myAsyncQuery(get(queryParamState));
},
});
atom을 직접 사용할 때 비동기 통신을 하는게 아닌 위 코드와 같이 seletor 안에서 비동기 통신 후 값을 사용할 수 있다.
Recoil 시작하기 | Recoil
React 애플리케이션 생성하기
recoiljs.org
'🍞 Front-End > React' 카테고리의 다른 글
[React] 리액트 프로젝트에 구글 폰트 적용하기 (0) | 2022.11.18 |
---|---|
[React] 리액트 JWT (JSON Web Token) (0) | 2022.11.08 |
[React] Uncaught TypeError index.js (0) | 2022.11.02 |
[MSW] 데이터 모킹 라이브러리 (Mock Service Worker) (0) | 2022.11.01 |
[Redux] React Redux 상태관리 라이브러리 (0) | 2022.11.01 |