React | Recoil 겉핥기
아래 영상을 보고 정리한 내용위주로 포스팅 해본다. 정말 재미있으니 영상을 보며 코드 따라쳐보기 추천박습니다.
Recoil?
Recoil은 페이스북 개발팀에서 만든 리액트 전용 상태 관리 라이브러리로, 상태관리를 가장 '리액트스럽게' 할 수 있도록 한다!
기존의 React 상태관리는 보통 Redux나 Mobx같은 외부 라이브러리를 사용하거나, React에서 기본적으로 제공하는 ContextAPI를 사용한다. 세 가지 모두 나쁘다고 할 순 없지만 사용 방법이 복잡하고, 비동기 통신을 위해서는 또 다른 라이브러리를 사용해야 하는 번거로움이 있다.
+) ContextAPI는 React에서 기본적으로 제공하는 상태 관리 솔루션이지만 각각의 데이터마다 context를 만들고, provider를 제공해야 하기 때문에 공유해야 할 데이터가 많아지면 많아질수록 context와 provider가 늘어나게 돼 점점 복잡해질 수 밖에 없다.
(ContextAPI는 단순하고 자주 변경되지 않는 데이터를 전역적으로 공유할 때는 여전히 유용하다.)
사용해보기
❗️Recoil은 함수형 컴포넌트에서만 사용할 수 있다.
RecoilRoot
Recoil을 사용할 컴포넌트는 RecoilRoot로 감싸야 한다. 중첩도 가능하며, RecoilRoot가 여러 개일 경우에는 가장 가까운 RecoilRoot를 참조한다.
import React from 'react';
import { RecoilRoot } from 'recoil';
import Counter from './Counter';
export default function App() {
return (
<RecoilRoot>
<Counter />
</RecoilRoot>
);
}
Atom
Atom은 하나의 데이터 조각(상태 값)이다. 컴포넌트가 구독하고, 해당 atom의 값이 변경될 때 마다 해당 atom을 구독하는 컴포넌트들이 다시 렌더링된다.
기존의 state와 크게 다른 값은 아니라고 생각된다..!
atom을 생성할 때는 atom메서드로 key와 default(기본 값)을 설정해 생성한다. redux에서 initialState를 지정할 때와 비슷..
// key값은 countState, 기본 값은 0으로 설정했다.
const countState = atom({
key: 'countState',
default: 0
});
useRecoilState로 atom의 값을 구독하고, 변경할 수 있다. useState Hook과 비슷하기 때문에 쉽게 사용할 수 있다.
// useRecoilState Hook (useState와 비슷함)
// 상태값, 상태값을 설정하는 함수
const [count, setCount] = useRecoilState(countState);
Selector
atom에서 파생된 데이터로 다른 atom에 의존하는 동적인 데이터를 만들 수 있게 해준다.
출처: ui.toast.com/weekly-pick/ko_20200616
selector는 atom에서 파생된 데이터 조각이자, 데이터를 반환하는 순수 함수이다.
아래에서 작성한 selector는 get을 이용해 countState를 이용한 데이터를 만들어낸다.
// 상태의 key와 getter함수를 전달(데이터를 읽는다)
const oddEvenState = selector({
key: 'oddEvenState',
get: ({ get }) => {
const count = get(countState); // getter를 이용해 countState를 가져옴
return count % 2 ? '홀' : '짝';
}
});
useRecoilValue로 읽을 수 있다. (상태를 설정(setter)하지 않고 atom의 값을 반환만 하는 hook)
// useRecoilValue는 읽기만 할 때 사용하는 Hook
const oddEven = useRecoilValue(oddEvenState);
selector는 비동기 액션에도 사용된다.
// state.js
import { selector } from 'recoil';
export const randomCat = selector({
key: 'randomCat',
get: async () => {
const response = await fetch('https://aws.random.cat/meow');
const data = await response.json();
return data.file;
}
});
// RandomCat.jsx
import React from 'react';
import { useRecoilValue, useRecoilValueLoadable } from 'recoil';
import { randomCat } from './state';
export default function RandomCat() {
const photoUrl = useRecoilValue(randomCat);
return (
<div className="random-cat">
<img src={photoUrl} alt="random cat" />
</div>
);
}
그리고 이럴 땐, <React.Suspense>로 감싸줘야 한다.
React Suspense는 자식 컴포넌트가 완전히 렌더링이 될 때 까지 기다려준다. fallback에는 자식 컴포넌트의 데이터가 로딩되는 동안 보여줄 컴포넌트를 전달한다.
// App.jsx
import React from 'react';
import { RecoilRoot } from 'recoil';
import RandomCat from './RandomCat';
export default function App() {
return (
<div className="App">
<h1>Random Cat</h1>
<p>페이지를 새로 고침할 때마다 랜덤한 고양이 사진을 보여줍니다!</p>
<RecoilRoot>
<React.Suspense fallback={null}>
<RandomCat />
</React.Suspense>
</RecoilRoot>
</div>
);
}
사용해보니 Redux보다 훠어어얼씬 편리한 것 같다.. 더 꾸준히 사용해보고 싶은 마음이 든다!!
🔑공식문서