리액트에는 많은 상태관리 라이브러리가 있다. 대표적으로 Redux, Modx가 있다. 하지만 이것들은 'React' 만을 위한 라이브러리가 아니라 다른 환경에서도 사용할 수 있다.
하지만 Recoil은 다른 라이브러리들과 달리 React 전용 상태관리 라이브러이며 hooks문법에 최적화 되어있다. hooks문법에 익숙한 사용자라면 빠른 시간 내에 배울 수 있다.
리액트의 상태들을 관리할 수 있는 방법은 많다. 리액트 자체에 내장된 Context API를 사용하는 방법도 있고 다른 라이브러리(Redux, Mobx 등)을 사용하는 방법도 있다.
create-react-app recoil_todolist --template typescript
npm install recoil
Recoil을 사용하려면 부모 컴포넌트에 RecoilRoot가 존재해야 한다. Redux의 Provider 같은 개념으로 루트 컴포넌트에 구성하는 것이 제일 좋은 방법이다.
import React from 'react';
import { RecoilRoot } from 'recoil';
import TodoForm from './TodoForm';
import TodoList from './TodoList';
const App = () => {
return (
<RecoilRoot>
<TodoForm />
<TodoList />
</RecoilRoot>
);
};
export default App;
atom은 Recoil에서 사용하는 상태를 정의한다. atom은 key(문자열)와 default(기본값)으로 구성되어 있으며 atom의 값을 사용하는 요소들은 atom에 내재적으로 등록되므로 상태가 업데이트 될 때마다 atom에 등록된 모든 구성 요소가 다시 렌더링되게 된다.
import { atom } from 'recoil';
export interface Todo {
id: number;
content: string;
}
export const todoStore = atom<Todo[]>({
key: 'todo',
default: [],
});
Todo List에 나타낼 Item이다. todo의 상태값을 변경하기 위해 useSetRecoilState라는 메서드를 사용했다. useSetRecoilState는 리액트 Hooks의 useState를 사용해 생성하는 setValue처럼 상태를 변경할 때 사용하는 메서드이다.
///src/component/TodoItem.tsx
import React from 'react';
import { useSetRecoilState } from 'recoil';
import { Todo, todoStore } from '../store/todo';
interface Props {
data: Todo;
}
const TodoItem = ({ data }: Props) => {
const setTodo = useSetRecoilState(todoStore);
const removeItem = () => {
setTodo((todo) => {
const newTodo = [...todo];
const index = todo.findIndex((v) => v.id === data.id);
if (index !== -1) {
newTodo.splice(index, 1);
}
return newTodo;
});
};
return (
<div>
<input type="checkbox" />
<span>{data.content}</span>
<span onClick={removeItem}>❌</span>
</div>
);
};
export default TodoItem;
Todo List에서는 아까 생성해놓은 상태를 useRecoilValue를 사용해 가져왔다. 지금 이 방식처럼 value와 setValue를 따로따로 가져올 수도 있지만 Hooks문법처럼 한번에 가져오는 방식도 있다. useRecoilState라는 메서드를 사용하게 되면 [value, setValue] = useRecoilState(todoState)의 형식으로 편리하게 가져올 수 있다.
// src/component/TodoList.tsx
import React from 'react';
import { useRecoilValue } from 'recoil';
import { todoStore } from '../store/todo';
import TodoItem from './TodoItem';
const TodoList = () => {
const todo = useRecoilValue(todoStore);
return (
<section>
{todo.map((v) => (
<TodoItem data={v} key={`todoData_${v.id}`} />
))}
</section>
);
};
export default TodoList;
이제 todo 상태값에 데이터를 추가할 폼을 작성했다. 여기서도 동일하게 useSetRecoilState를 사용해 데이터를 추가했다.
// src/component/TodoForm.tsx
import React, { useState } from 'react';
import { useSetRecoilState } from 'recoil';
import { todoStore } from '../store/todo';
const TodoForm = () => {
const setTodo = useSetRecoilState(todoStore);
const [content, setContent] = useState('');
const onSubmit = (e: React.FormEvent) => {
e.preventDefault();
setTodo((todo) => {
const id = todo.length ? todo[todo.length - 1].id + 1 : 0;
return [...todo, { id, content }];
});
};
const onChangeContent = (e: React.ChangeEvent<HTMLInputElement>) => {
setContent(e.target.value);
};
return (
<form onSubmit={onSubmit}>
<input type="text" onChange={onChangeContent} value={content} placeholder="내용" />
<button type="submit">입력</button>
</form>
);
};
export default TodoForm;
Recoil을 사용해본 결과, 굉장히 편하고 만족도가 높았다. 또한 기존의 React 문법과 비슷한 부분이 많아 쉽게 배울 수 있는 상태관리 라이브러기가 생겼다는 점이 좋았다. (Redux를 열심히 공부하고 있지만 정말 어려웠다... 그에 비해 Recoil은 배우기 엄청 쉬웠다) 프로젝트에서 한번 실전 적용을 해보고 비동기처리에 대해서도 더 공부해볼 예정이다. Recoil에서는 Selector 메서드를 제공해주는데 이 부분 또한 열심히 공부해봐야겠다.
**본 포스트는 다음 문서를 참고해 작성했습니다.
https://recoiljs.org/
https://medium.com/swlh/recoil-another-react-state-management-library-97fc979a8d2b
https://chanyeong.com/blog/post/10