[React] Recoil을 알아보자

이한형·2021년 12월 29일
1
post-thumbnail

Recoil


Recoil은 React의 상태관리 라이브러리중 하나입니다.
리액트를 개발한 페이스북 개발팀이 만들었고 react를 지원하는 전용 상태관리 라이브러리이기 때문에 react내부에 대한 접근이 가능하고 동시성 모드, Suspense 등을 손쉽게 지원 가능하다는 장점이 있습니다.
또한 러닝커브가 높은 Redux에 비해 러닝커브가 낮고 hook을 사용하는 방식이기때문에 쉽게 사용이 가능합니다.

why Recoil?

Redux를 사용을 한다고 하면 리듀서, 액션 타입, 액션 생성함수 이렇게 3가지 코드를 작성을 해야합니다.
이러한 작업은 귀찮기도하고 불변성을 지키면서 업데이트하는 부분도 힘들어지죠.
물론 immer나 redux toolkit을 이용하면 좀 더 간편하게 사용을 할 수 있습니다.

Recoil같은 경우에는 Flux 모델 기반인 리덕스와 달리 atmoic 모델 기반입니다.

상태들을 잘게 나눠서 관리를 합니다.
또한 Recoil은 위에서도 언급을 했지만 쉽게 사용이 가능하고 적은 코드로 사용이 가능합니다.

Recoil 사용하기

Recoil을 사용하기 위해선 recoil을 사용하는 컴포넌트의 부모 트리에는 RecoilRoot로 감싸줘야합니다.
보통 최상단 파일인 index.js에서 App을 감싸는 방식을 많이 사용합니다.

import React from 'react';
import ReactDom from 'react-dom'
import { RecoilRoot } from 'recoil
import App from './App';
import reportWebVitals from './reportWebVitals';

ReactDOM.render(
  <React.StrictMode>
  	<RecoilRoot>
    	<App />
  	</RecoilRoot>
  </React.StrictMode>,
  document.getElementById('root')
);

Atom

Recoil에서 Atom은 상태의 일부를 나타냅니다. atom은 어떤 컴포넌트에서나 읽고 쓰기가 가능합니다.
atom의 값을 읽는 컴포넌트들은 암묵적으로 atom을 구독하고 해당 atom의 상태에 변화가 생기면 해당 atom을 구독하는 모든 컴포넌트들이 리 렌더링됩니다.

const textState = atom({
  key: 'textState', // unique ID (with respect to other atoms/selectors)
  default: '', // default value (aka initial value)
});

useRecoilState

컴포넌트가 atom을 읽고 쓰게 하기 위해서는 useRecoilState()를 사용합니더.
useState hook과 유사하죠.

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>
  );
}

Selector

Selector는 파생된 상태의 일부를 나타냅니다. get을 이용해 atom의 정보들을 가져올 수 있고, 이를 통해 atom을 조합하여 새로운 데이터를 생성할 수 있습니다. 물론 해당 atom의 정보가 변경되면 해당 atom을 의존하는 Selector또한 자동으로 리렌더링이 됩니다.

const todoListFilterState = atom({
  key: 'todoListFilterState',
  default: 'Show All',
});

const filteredTodoListState = selector({
  key: 'filteredTodoListState',
  get: ({get}) => {
    const filter = get(todoListFilterState);
    const list = get(todoListState);

    switch (filter) {
      case 'Show Completed':
        return list.filter((item) => item.isComplete);
      case 'Show Uncompleted':
        return list.filter((item) => !item.isComplete);
      default:
        return list;
    }
  },
});

useRecoilValue

useRecoilValue는 atom의 값만 전달을 합니다.

function TodoList() {
  const todoList = useRecoilValue(todoListState);

  return (
    <>
      {/* <TodoListStats /> */}
      {/* <TodoListFilters /> */}
      <TodoItemCreator />

      {todoList.map((todoItem) => (
        <TodoItem key={todoItem.id} item={todoItem} />
      ))}
    </>
  );
}

useSetRecoilState

useSetRecoilState는 setter함수만을 반환합니다.

function TodoItemCreator() {
  const [inputValue, setInputValue] = useState('');
  const setTodoList = useSetRecoilState(todoListState);

  const addItem = () => {
    setTodoList((oldTodoList) => [
      ...oldTodoList,
      {
        id: getId(),
        text: inputValue,
        isComplete: false,
      },
    ]);
    setInputValue('');
  };

  const onChange = ({target: {value}}) => {
    setInputValue(value);
  };

  return (
    <div>
      <input type="text" value={inputValue} onChange={onChange} />
      <button onClick={addItem}>Add</button>
    </div>
  );
}

// 고유한 Id 생성을 위한 유틸리티
let id = 0;
function getId() {
  return id++;
}

결론

redux를 사용할때보다는 확실히 편리하지만 아직까지 버전이 안정화되어 있지는 않습니다.
또한 jotai와 같은 atomic 모델 기반이고 둘의 사용방법 또한 매우 비슷하여서 jotai를 학습하고 recoil을 학습해보니 확실히 둘이 비슷하다는 생각이 많이 들었습니다.
새로운 상태관리 라이브러리를 찾아보신다면 recoil또한 배워보시는 것을 추천합니다.

Recoil 공식문서

profile
풀스택 개발자를 지향하는 개발자

0개의 댓글