Frontend/React

React 상태 관리 : Recoil 로 해보자!

crayonzgrim 2021. 12. 4. 18:42
반응형

 

일반적으로 많은 회사에서 redux, mobx를 사용하는 얘기는 들었다.

나 또한 Redux, mobx는 경험이 있지만 Recoil은 얘기만 들어봤지 사용해보진 않았는데 그래서 documentation을 따라해보면서 알아가보려고 한다.

 

나는 머리가 좋지 않다.

그래서 하나하나 잘 따져가면서 공부를 해야한다.

redux를 익힐 때 고생했던 경험을 살려... 이번에는 redux를 익혔을 때의 순서를 지켜가면서 공부를 해봐야겠다


About Recoil

 

우선 각 상태관리 라이브러리들의 architecture 에 대해서 머리에 그림을 그려야 한다.

Redux의 경우, Flux architecture를 기반으로 했고, mobX의 경우 Proxy, Context API는 context를 기반으로 했다.

그렇다면 Recoil은? 바로 Atomic 이다.

 

Recoil은 React를 만든 Facebook에서 만들었다.

그러므로 더 잘 어울리게 만들었을 것이라는 믿음이 생기는 부분이었다.

 

Recoil은 React의 내부 상태만을 이용하게 된다.

아주 작은 atom 단위로 관리되며, selector라는 순수함수를 제공한다.

상태가 변경되면 atom을 참조하는 component만 re-rendering 되기 때문에 re-render의 최소화를 기대할 수 있다.


Let's use Recoil

한글화로 문서화가 잘 되어 있어서 Recoil 시작하기를 통해서 도전해보자!

우선 얼핏봐도 Redux에 비해 런닝커브가 비교적 낮다는 것을 알 수 있다.

 

cra를 통해서 리액트를 설치하고,

yarn create react-app recoil-tutorial

 recoil도 설치를 하자.

yarn add recoil

 

그리고 RecoilRoot를 만들어야 한다.

index.js에서 전체를 다 감싸주도록 하자.

// src > index.js

import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import { RecoilRoot } from "recoil";

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

 

components라는 폴더에 CharactorCounter.jsx, TextInput.jsx, CharacterCount.jsx를 만들어보자.

// src > components > TextInput.jsx

import React from "react";

const TextInput = () => {
  return (
    <div>
	TextInput
    </div>
  );
};

export default TextInput;
// src > components > CharacterCounter.jsx

import React from "react";

const CharacterCounter = () => {
  return <div>character count : 0</div>;
};

export default CharacterCounter;
// src > components > CharactorCounter.jsx

import React from "react";
import CharacterCount from "./CharacterCount";
import TextInput from "./TextInput";

const ChrarctorCounter = () => {
  return (
    <div>
      <TextInput />
      <CharacterCount />
    </div>
  );
};

export default ChrarctorCounter;

우선은 이렇게 만들어서 App.js에서 불러오자.

 

그리고 recoil 폴더를 만들어서 atom.js를 먼저 만들것이다.

// src > recoil > atom.js

import { atom } from "recoil";

export const textState = atom({
  key: "text",
  default: "",
});

// key : Unique ID
// default: initial value

atom은 이런 식으로 사용 할 수 있는데, key 값은 unique 해야하고, default 값은 initial value라고 생각하면 된다.

 

recoil의 atom은 state의 일부를 나타내는데 어떤 컴포넌트에서나 읽고 쓸 수 있다.

atom의 값을 읽게 되는 컴포넌트들은 암묵적으로 atom을 subscribe 한다.

그래서 atom의 업데이트는 subscribe 하고 있는 모든 컴포넌트를 re-rendering하게 만든다.

 

Atom이 만들어졌다면 Selector도 만들어보자.

selector는 atom state에 의존하는 동적인 데이터를 생성한다.

atom에 의존하므로 atom의 정보가 바뀌게 되면 selector도 자동으로 re-rendering 된다.

get이라는 함수를 사용하게 되어있는데 이를 통해서 atom의 정보를 가져올 수 있다.

그리고 set함수를 통해서 atom 정보를 업데이트 하도록 할 수 있다.

// src > recoil > selector.js

import { selector } from "recoil";
import { textState } from "../atoms/atom";

export const charCountState = selector({
  key: "charCountState", 
  get: ({ get }) => {
    const text = get(textState);

    return text.length;
  },
});

 

그렇다면 이를 사용해보자.


 [ 그 전에 잠깐!!! recoil에서 사용되어지는 hook을 짧게 살펴보자 ]

 

1. useRecoilState

기본 값 대신 Recoil state를 인수로 받는다는 점만 제외하면 useState hook과 비슷하다고 볼 수 있다.
이는 컴포넌트가 상태를 읽고 쓰려고 할 때 사용이 권장 되어진다.

example) const [text, setText] = useRecoilState(textState)

2. useSetRecoilState

Recoil state value(subscribe하는 value)를 업데이트하기 위한 setter 함수를 반환한다.

이 hook은 컴포넌트가 상태를 읽지 않고 쓰기만 하려고 할 때 권장되어진다.

example)
function Form() {
  const setNamesState = useSetRecoilState(namesState); 
  return <FormContent setNamesState={setNamesState} />;
}

위 예시의 컴포넌트는 마운트 될 때 한번만 랜더링 되어질 것이다.

 

3. useRecoilValue

주어진 Recoil state value를 반환한다.

이는 읽기 전용 상태, 쓰기 가능 상태에서 모두 동작하므로 컴포넌트가 상태를 읽을 수만 있게 하고 싶을 때 사용 권장이 되어지는 hook이다.

값의 업데이트를 할 필요 없는 경우 사용 할 수 있을 것이다.

또한 React에서 사용하면 상태가 업데이트 될 때 re-rendering 되도록 컴포넌트를 subscribe 하고, state가 error를 가지고 있거나, 보류 중인 비동기 resolution이 있다면 던져줄 수 있다.

 

4. useResetRecoilState

이는 비동기 selector의 값을 읽기 위해 사용 된다.

상태를 기본값으로 reset 할 수 있게 해준다.

// example)
import {todoListState} from "../atoms/todoListState";

const TodoResetButton = () => {
  const resetList = useResetRecoilState(todoListState);
  return <button onClick={resetList}>Reset</button>;
};

다시 돌아와서, 앞서 만든 TextInput.jsx에 useRecoilState를 사용해볼 수 있다.

// src > components > TextInput.jsx

import React from "react";
import { useRecoilState } from "recoil";
import { textState } from "../recoil/atoms/atom";

const TextInput = () => {
  const [stateText, setStateText] = useRecoilState(textState);

  const changeHandler = (e) => {
    setStateText(e.target.value);
  };

  return (
    <div>
      <input type="text" value={stateText} onChange={changeHandler} />
      <br />
      Echo: {stateText}
    </div>
  );
};

export default TextInput;

앞서 설명 했던 Recoil의 hook 중 useRecoilState를 사용해서  만들었다.

 

이어서 CharacterCount.jsx를 만들어보자

import React from "react";
import { useRecoilValue } from "recoil";
import { charCountState } from "../recoil/selectors/selector";

const CharacterCounter = () => {
  const count = useRecoilValue(charCountState);

  return <div>character count : {count}</div>;
};

export default CharacterCounter;

이는 useRecoilValue를 사용하여서 만들 수 있다.

그러면 아래처럼 사용이 가능하다.

이것이 Recoil의 기초적인 사용 방법이다.

반응형