React 애플리케이션에서 전역 상태를 관리하고, 복잡한 상태 로직을 단순화하기 위해 설계되었다. React의 기본 상태 관리 메커니즘과 긴밀하게 통합되어 있으며, 상태를 효율적으로 공유하고 관리할 수 있는 기능을 제공한다.
npm install recoil
// Atom 정의
import React from 'react';
import { atom } from 'recoil';
const textState = atom({
key: 'textState', // 고유한 ID
default: '', // 초기값
});
import React from 'react';
import { selector } from 'recoil';
const charCountState = selector({
key: 'charCountState', // 고유한 ID
get: ({ get }) => {
const text = get(textState);
return text.length;
},
});
import React from "react";
import { RecoilRoot } from "recoil";
import CharacterCounter from "./CharacterCounter";
function App() {
return (
<RecoilRoot>
<CharacterCounter />
</RecoilRoot>
);
}
useRecoilState 훅을 통해 atom의 값을 읽고 업데이트 할 수 있다.
import React from 'react';
import { useRecoilState } from 'recoil';
function TextInput() {
const [text, setText] = useRecoilState(textState);
const onChange = (event) => {
setText(event.target.value);
};
return (
<div>
<input type="text" value={text} onChange={onChange} />
<br />
Echo: {text}
</div>
);
}
selector을 사용하는 컴포넌트에서는 useRecoilValue 훅을 사용하여 selector 값을 읽을 수 있다.
import React from 'react';
import { useRecoilValue } from 'recoil';
function CharacterCount() {
const count = useRecoilValue(charCountState);
return <>Character Count: {count}</>;
}
import React from 'react';
import { RecoilRoot, atom, selector, useRecoilState, useRecoilValue } from 'recoil';
// Atom 정의
const textState = atom({
key: 'textState', // 고유한 ID
default: '', // 기본값
});
// Selector 정의
const charCountState = selector({
key: 'charCountState', // 고유한 ID
get: ({ get }) => {
const text = get(textState);
return text.length;
},
});
function CharacterCounter() {
return (
<div>
<TextInput />
<CharacterCount />
</div>
);
}
function TextInput() {
const [text, setText] = useRecoilState(textState);
const onChange = (event) => {
setText(event.target.value);
};
return (
<div>
<input type="text" value={text} onChange={onChange} />
<br />
Echo: {text}
</div>
);
}
function CharacterCount() {
const count = useRecoilValue(charCountState);
return <>Character Count: {count}</>;
}
function App() {
return (
<RecoilRoot>
<CharacterCounter />
</RecoilRoot>
);
}
export default App;
Recoil은 React의 Context API와 유사한 방식으로 상태를 관리한다.
컴포넌트 내에서 상태를 정의하고 사용할 수 있으며, 다른 컴포넌트에서도 해당 상태를 사용할 수 있다.
=> Redux와 같이 별도의 상태 관리 라이브러리를 사용하지 않아도 된다!
Promise나 async/await를 사용해 비동기 처리를 할 수 있으며, 상태 변화를 감지하여 자동으로 컴포넌트를 업데이트 한다.
개발자 도구를 제공하여 상태 변화를 쉽게 추적하고 디버깅할 수 있다.
Recoil의 API는 간단하고 직관적이다. atom, selector, useRecoilState, useRecoilValue 등의 간단한 함수들을 사용하여 상태를 관리할 수 있다.
Recoil은 React의 Suspense와 같은 기능을 완벽하게 지원하여, 비동기 데이터 로딩을 자연스럽게 처리할 수 있다.
프로젝트 규모가 커질수록 상태의 구조와 의존성을 관리하는 것이 어려워질 수 있다.
loadable은 주로 비동기 셀렉터와 함께 사용되어, 비동기 작업의 현재 상태를 확인하고 적절한 UI를 렌더링하는 데 유용하다.
loadable은 Recoil의 비동기 셀렉터로부터 반환된 상태를 감싸고, 다음 세 가지 상태를 제공한다.
import React from 'react';
import { atom, selector, useRecoilValueLoadable, RecoilRoot } from 'recoil';
// 사용자 데이터를 비동기로 가져오는 비동기 셀렉터 정의
const userIdState = atom({
key: 'userIdState',
default: 1,
});
const userState = selector({
key: 'userState',
get: async ({ get }) => {
const userId = get(userIdState);
const response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`);
if (!response.ok) {
throw new Error('Network response was not ok');
}
const user = await response.json();
return user;
},
});
function UserInfo() {
const userLoadable = useRecoilValueLoadable(userState);
switch (userLoadable.state) { // userLoadable.state를 사용하여 로딩, 성공 및 에러 상태를 처리한다.
case 'loading': // loading 상태일 때는 "Loading..." 메시지를 표시한다.
return <div>Loading...</div>;
case 'hasValue': // hasValue 상태일 때는 사용자 정보를 표시한다.
return (
<div>
<h1>{userLoadable.contents.name}</h1>
<p>{userLoadable.contents.email}</p>
</div>
);
case 'hasError': // hasError 상태일 때는 에러 메시지를 표시한다.
return <div>Error: {userLoadable.contents.message}</div>;
default:
return null;
}
}
function App() {
return (
<RecoilRoot>
<UserInfo />
</RecoilRoot>
);
}
export default App;
Recoil 라이브러리에서 제공하는 훅으로, 주어진 Recoil 상태(Atom 또는 Selector)의 현재 값을 읽어오고, 그 상태가 비동기적인 경우 로딩 상태, 성공 상태, 에러 상태를 쉽게 처리할 수 있도록 도와준다.
이 훅은 주어진 상태의 값을 loadable 객체로 반환하며, 이 객체는 상태가 loading, hasValue, hasError 중 어느 상태인지 확인할 수 있는 방법을 제공한다.
=> 비동기 작업을 간편하게 처리하고, UI를 상태에 맞게 렌더링할 수 있다.
loadable을 사용하면 비동기 작업의 다양한 상태를 쉽게 관리할 수 있다.
로딩, 성공, 에러 상태에 따라 분기 처리를 명확하게 할 수 있어, 비동기 작업을 처리하는 로직이 직관적이다.
React의 렌더링 흐름과 자연스럽게 통합되어, 비동기 작업의 상태에 따라 적절한 UI를 쉽게 구현할 수 있다.
Selector는 일반적으로 비동기 데이터를 가져오는 데 사용된다.
import { selector } from 'recoil';
const userState = selector({
key: 'userState',
get: async ({ get }) => {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/users/1');
if (!response.ok) {
throw new Error('Network response was not ok');
}
const user = await response.json();
return user;
} catch (error) {
throw error;
}
},
});
비동기 데이터의 상태를 확인하고, 로딩 상태, 데이터가 있을 때의 처리, 에러 상태 등을 쉽게 관리할 수 있다.
import React from 'react';
import { useRecoilValueLoadable } from 'recoil';
import { userState } from './atoms'; // Recoil에서 정의한 selector 또는 atom 가져오기
function UserInfo() {
const userLoadable = useRecoilValueLoadable(userState);
switch (userLoadable.state) {
case 'loading':
return <div>Loading...</div>;
case 'hasValue':
return (
<div>
<h1>{userLoadable.contents.name}</h1>
<p>{userLoadable.contents.email}</p>
</div>
);
case 'hasError':
return <div>Error: {userLoadable.contents.message}</div>;
default:
return null;
}
}
export default UserInfo;
useRecoilCallback 훅을 사용하면 Recoil 상태를 업데이트하는 비동기 함수를 생성할 수 있다. 이 방법은 상태 업데이트가 필요한 비동기 작업에 유용하다.
import { atom, useRecoilState, useRecoilCallback } from 'recoil';
import React from 'react';
const userIdState = atom({
key: 'userIdState',
default: 1,
});
const userState = atom({
key: 'userState',
default: null,
});
function UserInfo() {
const [userId, setUserId] = useRecoilState(userIdState);
const [user, setUser] = useRecoilState(userState);
const fetchUser = useRecoilCallback(({ snapshot, set }) => async () => {
const id = await snapshot.getPromise(userIdState);
const response = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`);
const userData = await response.json();
set(userState, userData);
});
React.useEffect(() => {
fetchUser();
}, [userId]);
return (
<div>
{user ? (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
) : (
<div>Loading...</div>
)}
</div>
);
}
export default UserInfo;
=> Recoil에서 비동기 데이터를 받아올 때, selector와 useRecoilValueLoadable을 주로 활용하여 상태를 관리한다. selector는 비동기 데이터를 가져오는 역할을 하고, useRecoilValueLoadable은 해당 데이터의 상태를 관리하며 UI를 처리하는 데 사용된다.
useRecoilCallback을 통해 비동기 작업을 수행하고 Recoil 상태를 업데이트하는 경우도 있다.
Recoil에서 로딩, 성공, 에러와 관련된 처리를 useRecoilValueLoadable 훅을 사용하여 처리할 수 있다. useRecoilValueLoadable은 loadable 객체를 반환하며, 이 객체는 비동기 작업의 현재 상태를 나타낸다.
import React from 'react';
import { atom, selector, useRecoilValueLoadable, RecoilRoot } from 'recoil';
// 사용자 ID를 저장하는 Atom 정의
const userIdState = atom({
key: 'userIdState', // 고유한 키
default: 1, // 기본값
});
// 사용자 데이터를 비동기로 가져오는 Selector 정의
const userState = selector({
key: 'userState',
get: async ({ get }) => {
const userId = get(userIdState);
const response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`);
if (!response.ok) {
throw new Error('Network response was not ok');
}
const user = await response.json();
return user;
},
});
function UserInfo() {
// useRecoilValueLoadable 훅을 사용하여 비동기 상태를 관리
const userLoadable = useRecoilValueLoadable(userState);
// 상태에 따라 UI를 렌더링
switch (userLoadable.state) {
case 'loading':
return <div>Loading...</div>;
case 'hasValue':
return (
<div>
<h1>{userLoadable.contents.name}</h1>
<p>{userLoadable.contents.email}</p>
</div>
);
case 'hasError':
return <div>Error: {userLoadable.contents.message}</div>;
default:
return null;
}
}
function App() {
return (
<RecoilRoot>
<UserInfo />
</RecoilRoot>
);
}
export default App;
Redux는 JavaScript 애플리케이션의 상태를 예측 가능하게 관리하기 위한 상태 관리 라이브러리이다.
단일 스토어와 불변 객체 패턴을 사용하여 상태를 관리하며, 상태 변경은 액션(action)을 통해 이루어진다. Redux의 핵심 개념에는 Store, Action, Reducer, Middleware 등이 포함된다.
Recoil은 Facebook에서 개발한 React 상태 관리 라이브러리로, React 컴포넌트의 상태를 효율적으로 관리하기 위한 목적으로 설계되었다.
Recoil은 원자(atom) 단위의 상태를 중심으로 구성되며, 전역 상태를 간편하게 공유하고 관리할 수 있는 API를 제공한다.
Redux: 복잡한 상태 관리가 필요하고, 예측 가능한 데이터 흐름을 중요시하는 경우에 적합하다. 큰 규모의 애플리케이션에서 특히 유용하며, 개발자 도구 지원이 강력한 점도 장점이다.
Recoil: React와 통합이 잘 되어 있고, 원자 단위의 상태 관리와 비동기 데이터 처리를 간편하게 처리할 수 있는 경우에 유용하다. 상대적으로 최신 기술이지만, 간편한 사용법과 성능 최적화를 제공한다는 점이 큰 장점이다.