공식 문서 :https://recoiljs.org/ko/docs/introduction/getting-started/
혼자서 가벼운 프로젝트를 하려고 할 때, Redux tookit과 Recoil중 어떤 상태관리 라이브러리를 사용해야 할지 정말 고민이 되었다. 둘다 많이 사용되는 유명한 라이브러리고 React 자체를 처음 사용해보는 내 입장에서는 비교적 가볍다고 여겨지는 Recoil을 사용하기로 결정했다.
Recoil이란 가볍고 간편한 전역 상태 관리 라이브러리다. React 애플리케이션에서 전역 상태를 효율적으로 관리할 수 있도록 해준다.
npm install recoil
yarn add recoil
recoil을 처음 사용한다면 recoil을 사용하는 컴포넌트는 <RecoliRoot>
이라는 부모 컴포넌트가 필요하다.
import React from 'react';
import {
RecoilRoot,
atom,
selector,
useRecoilState,
useRecoilValue,
} from 'recoil';
function App() {
return (
<RecoilRoot>
<CharacterCounter />
</RecoilRoot>
);
}
제일 넣기 좋은 장소는 React의 제일 상위컴포넌트인 App() 컴포넌트이다. 이곳에 위치시키면 내가 만드는 모든 컴포넌트에서 Recoil로 상태관리를 할 수 있다.
이제 상태를 관리할 시간이다. 만약 유저의 상태를 전역으로 관리하고 싶다면 다음과 같은 코드를 작성할 수 있다.
// atoms.js
import { atom } from 'recoil';
export const userState = atom({
key: 'userState',
default: {
id:"",
firstName:"",
lastName:"",
email:"",
gender:"",
age:0,
}
,
});
key
: 전역 상태를 나타내는 유일한 아이디로 작성해야한다.default
: 기본 값컴포넌트가 atom을 읽고 쓰게 하기 위해서는 useRecoilState()를 사용하면 된다.
import React from 'react';
import { useRecoilState } from 'recoil';
import { userState } from './atoms';
function UserForm() {
const [user, setUser] = useRecoilState(userState); //전역 상태 관리
const handleChange = (event) => {
const { name, value } = event.target;
setUser((prevUser) => ({
...prevUser,
[name]: value,
}));
};
return (
<div>
<h2>User Form</h2>
<form>
<label>
ID:
<input type="text" name="id" value={user.id} onChange={handleChange} />
</label>
<br />
<label>
firstName:
<input type="text" name="name" value={user.name} onChange={handleChange} />
</label>
<br />
<label>
lastName:
<input type="text" name="name" value={user.name} onChange={handleChange} />
</label>
<br />
<label>
Email:
<input type="email" name="email" value={user.email} onChange={handleChange} />
</label>
<br />
<label>
Gender:
<input type="text" name="gender" value={user.gender} onChange={handleChange} />
</label>
<br />
<label>
Age:
<input type="number" name="age" value={user.age} onChange={handleChange} />
</label>
</form>
</div>
);
}
위의 예시 코드에서는 유저정보를 담는 atom을 사용했다. 그런데 만약 로그아웃을 한다면 atom에 담긴 유저정보를 초기화 시킬 필요가 있을 것이다. 그때 사용하는 것이 useResetRecoilState() 이다.
import React from 'react';
import { useResetRecoilState } from 'recoil';
import { userState } from './atoms';
function ResetButton() {
const resetUserState = useResetRecoilState(userState);
const handleReset = () => {
resetUserState();
};
return (
<button onClick={handleReset}>logout</button>
);
}
Recoil에서는 Seletor를 사용하여 파생된 상태를 계산하고 관리할 수 있다. 이게 무슨말이냐하면 다른 원자의 값을 읽어와서 내 입맛대로 바꿀 수 있다는 이야기 이다.
가령 다음과 같은 userState가 있다고 해보자
// atoms.js
import { atom } from 'recoil';
export const userState = atom({
key: 'userState',
default: {
id:"",
firstName:"",
lastName:"",
email:"",
gender:"",
age:0,
}
,
});
우리는 selector를 사용해서 원하는 값을 내맘 대로 가공해서 꺼낼 수 있다.
import { selector } from 'recoil';
import { userState } from './atoms';
export const fullNameSelector = selector({
key: 'fullNameSelector',
get: ({ get }) => {
const user = get(userState); //useState선택
return `${user.firstName} ${user.lastName}`; //반환
},
});
useRecoilValue를 사용해 selector에서 값을 꺼낼 수 있다.
import React from 'react';
import { useRecoilValue } from 'recoil';
import { fullNameSelector } from './selectors';
function UserProfile() {
const fullName = useRecoilValue(fullNameSelector);
return (
<div>
<h2>User Profile</h2>
<p>Full Name: {fullName}</p>
</div>
);
}
selector에서 가공한 값을 useRecoilValue를 사용해 fullName 변수에 할당하여 사용자이름을 표시할 수 있다.
물론 selector가 의존하는 상태 원자의 값이 변경될 때마다 적절하게 업데이트된다.
atom Eeffects는 부수 효과를 관리하고 Recoil의 atom을 초기화 또는 동기화 하기 위한 API 이다.
다음은 브라우저의 localStorage와 atom을 동기화 하는 간단한 예시이다.
const localStorageEffect = key => ({setSelf, onSet}) => {
const savedValue = localStorage.getItem(key)
if (savedValue != null) {
setSelf(JSON.parse(savedValue));
}
onSet((newValue, oldValue, isReset) => { //atom이 변경될때만 호출
isReset
? localStorage.removeItem(key)
: localStorage.setItem(key, JSON.stringify(newValue));
});
};
const currentUserIDState = atom({
key: 'CurrentUserID',
default: "1",
effects: [
localStorageEffect('current_user'),
]
});
localStorageEffect
: atom의 effects옵션에서 사용할 함수를 정의했다.