recoil리액트 심화/recoil2022. 6. 14. 00:47
Table of Contents
recoil
- 페이스북에서 출시한 React 전용인 전역 상태 관리 라이브러리이다.
- 공식 레퍼런스 ↓↓↓
https://recoiljs.org/ko/docs/introduction/motivation
기존 전역 상태관리 라이브러리의 문제
- 기존에 사용되었던 Redux나 Mobx가 성능 자체에 문제가 있던 것이 아니라 오히려 Flux 패턴을 기반으로 안정적이지만 여러가지 문제가 존재했다
- React 전용 라이브러리가 아니기에 React가 볼 때 Store가 외부의 어떤 것이며 동시성 모드를 구현하기에 호환성이 떨어진다
- 복잡한 보일러 플레이트가 존재해서 러닝커브가 높다.
- 비동기 데이터를 호출하기 위해서 서드파티 라이브러리가 필요하다 Redux-Thunk, Redux-Saga 등
장점
- React 전용 라이브러리여서 React 내부에 접근이 용이하다. 특히 동시성 모드, Suspense 등을 지원해서 UX 관점에서도 유리한 웹 어플리케이션을 만들 수 있다.
- 러닝커브가 낮으며 전역 상태를 정의하고 설정하기가 쉽고 recoil에서 사용하는 훅들로 상태를 get/set하기 때문에 리액트 문법과도 매우 유사하다
- 보일러 플레이트가 매우 적다
recoil 주요 개념
Atoms
- Atom은 상태(state)의 일부를 나타낸다.
- Atoms는 어떤 컴포넌트에서나 읽고 쓸 수 있다. atom의 값을 읽는 컴포넌트들은 암묵적으로 atom을 구독한다.
- 상태의 단위이며 업데이트와 구독이 가능하다.
- Atom이 업데이트되면 각각의 구독된 컴포넌트는 새로운 값을 가지고 리렌더링된다.
- Atom은 각 컴포넌트의 useState 대신 사용할 수 있다.
- 동일한 Atom이 여러 컴포넌트에서 사용되면 모든 컴포넌트는 상태를 공유한다. (전역 상태)
Selectors
- Selector는 파생된 상태의 일부를 나타낸다. 파생된 상태는 상태의 변화다. 파생된 상태를 어떤 방법으로든 주어진 상태를 수정하는 순수 함수에 전달된 상태의 결과물로 생각할 수 있다
- atoms과 다른 selectors를 입력으로 받는 순수 함수이다.
- 상위 atoms 또는 selectors가 업데이트 된다면 하위 selector 함수도 다시 실행된다!!
- 컴포넌트는 selector를 atom처럼 구독할 수 있고 selector가 변경되면 컴포넌트도 다시 리렌더링된다.
- selector는 상태를 기반으로 하는 파생된 데이터들을 계산할 때 사용된다. 최소한의 상태 집합들만 atoms에 저장하고 다른 파생되는 데이터는 selector에 명시된 함수를 통해 효율적으로 계산해서 쓸모없는 상태에 대한 보존을 방지한다.
- selector는 어떤 컴포넌트가 자신을 필요로하며 자신은 어떤 상태를 의존하지는 추적하기 때문에 함수적인 접근 방식을 매우 효율적으로 하게 한다
Recoil 사용하기
1. recoil 설치하기
yarn add recoil
2. RecoilRoot 로 전체 컴포넌트 감싸기
- index.js에서 RecoilRoot로 App 컴포넌트를 감싸자
index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import {RecoilRoot} from "recoil";
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<RecoilRoot>
<App/>
</RecoilRoot>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
3. atom 사용하기
- src 폴더 아래 recoil 폴더를 만들고 store.js 파일을 만들어보자.
- atom을 이용해서 상태를 정의할 때는 key(고유값)과 default(기본값)을 설정해준다.
- key는 리액트가 atom을 식별하기 위한 고유의 ID이므로 atom 사이에서 중복된 key가 존재하면 안된다!
src/recoil.store.js
import {atom} from "recoil";
export const countState = atom({
key: "countState",
default: 1
});
- redux처럼 따로 리듀서와 액션생성 함수를 사용하지 않아도 손쉽게 전역상태를 사용할 수 있다
useRecoilState
- 컴포넌트에서 useRecoilState를 사용하면 useState 훅을 사용하는 것처럼 전역 상태를 사용할 수 있다!
- 전역 상태와, 상태를 업데이트할 함수를 useRecoilState 훅에 atom을 넣어서 반환받을 수 있다!
const [count, setCount] = useRecoilState(countState);
- 아래처럼 useRecoilState 훅으로 countState란 atom에 저장되어 있는 count를 1씩 증가시켜보자!
import React from "react";
import {useRecoilState} from "recoil";
import {countState} from "./recoil/store";
const App = () => {
const [count, setCount] = useRecoilState(countState);
return(
<React.Fragment>
<h1>count: {count} </h1>
<button onClick={()=>{
setCount(count + 1);
}}>
1증가
</button>
</React.Fragment>
)
}
export default App;
4. selector 사용하기
- select를 사용해서 어떤 atom을 구독해서 selector에서 atom의 상태를 조작한 값을 반환할 수 있고 컴포넌트에서 useRecoilValue 훅으로 데이터를 가져올 수 있다
- 구독한 atom의 값이 변할 때마다 자동으로 조작된 값을 리턴한다!
- 아래 예시는 countState Atom을 구독해서 해당 atom의 값 * 4를 리턴하는 예시이다!
import {atom, selector} from "recoil";
export const countState = atom({
key: "countState",
default: 1
});
export const multiCountState = selector({
key: "multiCountState",
get:({get}) => {
// get()은 다른 atom을 구독!
return get(countState) * 4;
}
})
- 컴포넌트에서 countState atom의 상태 값을 1 증가시키면 multiCountState selector의 값은 4씩 늘어난다!!
import React from "react";
import {useRecoilState, useRecoilValue} from "recoil";
import {countState, multiCountState} from "./recoil/store";
const App = () => {
const [count, setCount] = useRecoilState(countState);
const multiCount = useRecoilValue(multiCountState)
return(
<React.Fragment>
<h1>count: {multiCount} </h1>
<button onClick={()=>{
setCount(count + 1);
}}>
1증가
</button>
</React.Fragment>
)
}
export default App;
5. selectorFamily 사용하기
- selectorFamily를 사용하면 selector의 파라미터에 컴포넌트의 state나 값을 넣어서 atom을 조작할 수 있다!
- selectorFamily에 전달된 인자가 변경되면 자동으로 selectorFamily의 값이 반환된다!
- 아래 예시는 selectorFamily에 전달된 파라미터에 구독한 atom의 값을 더해서 반환하는 예시이다!
import {atom, selector, selectorFamily} from "recoil";
export const countState = atom({
key: "countState",
default: 1
});
export const addCountState = selectorFamily(
{
key: "addCountState",
get: (num) =>
({get}) => {
return get(countState) + Number(num);
}
}
)
- 컴포넌트의 state를 selectorFamily의 파라미터로 넘기고 state가 변할 때마다 조작된 값이 리턴된다!!
import React, {useState, useRef} from "react";
import {useRecoilValue} from "recoil";
import {addCountState} from "./recoil/store";
const App = () => {
const [num, setNum] = useState(0)
const multiCount = useRecoilValue(addCountState(num))
return(
<React.Fragment>
<h1>count: {multiCount}</h1>
<input
onChange={(e)=>(setNum(e.target.value))}/>
<span>count에 더하기</span>
</React.Fragment>
)
}
export default App;
+++ recoil-persist
yarn add recoil-persist
- 웹 스토리지를 이용하여 recoil의 상태를 페이지가 새로고침 되어도 가지고 있을 수 있다!
- recoil-persist를 사용해서 atom을 만들면 상태가 변화될 때마다 localStorage에 저장되고 새로고침 되도 해당 값을 가져다 쓸 수 있다!!
- 아래 예시는 persistAtom으로 atom을 생성한 후 컴포넌트에서 atom의 상태를 변화하는 함수를 호출할 때마다 localStorage에 상태가 자동으로 저장되고 새로고침해도 그대로 남아있는 예시이다!!
import {atom, selector, selectorFamily} from "recoil";
import { recoilPersist } from 'recoil-persist';
// recoil-persist
const {persistAtom} = recoilPersist();
export const tokenState = atom({
key: 'tokenState',
default: '',
effects_UNSTABLE: [persistAtom]
})
export const countState = atom({
key: "countState",
default: 1
});
export const addCountState = selectorFamily(
{
key: "addCountState",
get: (num) =>
({get}) => {
return get(countState) + Number(num);
}
}
)
- 컴포넌트에서 input의 value를 persist-atom의 상태로 변화시킨다
import React, {useState, useRef} from "react";
import {useRecoilValue, useRecoilState} from "recoil";
import {addCountState, countState, tokenState} from "./recoil/store";
const App = () => {
const [num, setNum] = useState(0)
const [count, setCount] = useRecoilState(countState)
const multiCount = useRecoilValue(addCountState(num))
const [token, setToken] = useRecoilState(tokenState)
return(
<React.Fragment>
<h1>atom: {count}</h1>
<h1>count: {multiCount}</h1>
<input
onChange={(e)=>(setNum(e.target.value))}/>
<span>count에 더하기</span>
<h1>localStorage: {token}</h1>
<input onChange={(e)=>{setToken(e.target.value)}}/>
<span>localStorage 값 바꾸기</span>
</React.Fragment>
)
}
export default App;
- atom의 상태가 변경될 때마다 자동으로 localStorage에 반영된다!
@덕구공 :: Duck9s'
주니어 개발자에욤
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!