recoil 찍먹해보기

이창호·2022년 5월 31일
0
post-thumbnail

React에는 여러 상태 관리툴이 있고, 그 중 대표적인 것이 redux 입니다.
저 또한 redux를 사용하고 있는 중인데, React를 만든 페이스북에서 상태 관리 솔루션으로 제시한 recoil 이라는 라이브러리가 있어서 한번 사용해보려 해요.


  • 설치
npm install recoil
  • 적용 (redux와 마찬가지로 root가 필요해요)
import App from "./App";
import { RecoilRoot } from "recoil";

<RecoilRoot>
	<App />
</RecoilRoot>

recoil 알아보기

atom - 상태의 유니크id와 초기값을 설정할 수 있어요.

import { atom } from "recoil";

export const todoState = atom({
  key: "todoState", // id
  default: [], // initial value
});

useRecoilValue - 상태(atom) 값을 가져와요. (상태조작 X)

import { useRecoilValue } from "recoil";
import { todoState } from "../store/atom";

const todoList = useRecoilValue(todoState);

useSetRecoilState - 상태(atom) 값을 변경해요.

import React, { useState } from "react";
import { useSetRecoilState } from "recoil";
import { todoState } from "../store/atom";

const TodoInput = () => {
  const [input, setInput] = useState("");
  const setTodoInput = useSetRecoilState(todoState);

  const updateTodo = () => {
    setTodoInput((prevState) => [
      ...prevState,
      {
        text: input,
      },
    ]);
    setInput("");
  };

  const handleChange = (event) => setInput(event.target.value);

  return (
    <React.Fragment>
      <div>
        <input type="text" value={input} onChange={handleChange} />
        <button onClick={updateTodo}>update</button>
      </div>
    </React.Fragment>
  );
};

export default TodoInput;

selector - 상태(atom) 에 의존하여 데이터를 다룰 수 있게 해줘요.

  • 필터에 관한 설정
// 필터링 상태
const todoFilterState = atom({
  key: "todoFilterState",
  default: "Show All",
});

// 필터링 할 목록
const filterTodoState = selector({
  key: "filterTodoState",
  get: ({ get }) => {
    const filter = get(todoFilterState);
    const list = get(todoState);

    switch (filter) {
      case "Show Completed":
        return list.filter((item) => item.isComplete);
      case "Show Uncompleted":
        return list.filter((item) => !item.isComplete);
      default:
        return list;
    }
  },
});
  • 필터 조작하기
import React from "react";
import { useRecoilState } from "recoil";
import { todoFilterState } from "../store/atom";

function TodoListFilters() {
  const [filter, setFilter] = useRecoilState(todoFilterState); 

  const updateFilter = (event) => setFilter(event.target.value);

  return (
    <>
      Filter:
      <select value={filter} onChange={updateFilter}>
        <option value="Show All">All</option>
        <option value="Show Completed">Completed</option>
        <option value="Show Uncompleted">Uncompleted</option>
      </select>
    </>
  );
}
export default TodoListFilters;
  • 필터 된 값 사용
const todoList = useRecoilValue(filterTodoState);

todoList.map((item) => <TodoItem item={item} />

그리고 redux 처럼 thunk나 saga같은 별도의 설치 필요 없이 비동기도 다룰 수 있어요.

  • 상태 선언 및 변경
// atom
export const pokemonState = atom({
  key: "pokemonState",
  default: "",
});

// pokemonView
import { useRecoilState } from "recoil";
import { pokemonState } from "./store/atom/pokemon";

const [pokemonName, setPokemonName] = useRecoilState(pokemonState);
  const pokemonRef = useRef();
  const handleSearch = () => setPokemonName(pokemonRef.current.value);
  
  return (
  	<>
    	<input type="text" ref={pokemonRef} />
		<button onClick={handleSearch}>search</button>
        <Suspense fallback={"...loading"}>
        	<PokeInfo /> // 검색 시 데이터 뷰
      	</Suspense>
    </>
  )
  • 상태변경에 따른 데이터 호출
// atom
export const getPokemon = selector({
  key: "getPokemon",
  get: async ({ get }) => {
    const name = get(pokemonState);

    if (!name) {
      return;
    }

    try {
      const response = await fetch(`https://pokeapi.co/api/v2/pokemon/${name}`);
      const result = response.json();
      return result;
    } catch (error) {
      throw Error("이름을 입력해 주세요.");
    }
  },
});

// pokemonInfo
import { useRecoilValue } from "recoil";
import { getPokemon } from "./store/atom/pokemon";

const pokemon = useRecoilValue(getPokemon);

  return (
    <>
      <p>포켓몬번호: {pokemon.order}</p>
      <p>키: {pokemon.height}</p>
      <p>무게: {pokemon.weight}</p>
      <p>능력:</p>
          <ul>
              {pokemon.abilities.map((item, index) => (
                  <li key={index}>{item.ability.name}</li>
              ))}
          </ul>
    </>
  )
  1. 검색할 포켓몬 이름을 저장할 atom을 생성 (초기값은 빈값)
  2. input의 값이 변경되고, 검색버튼을 누를 시 atom의 값을 변경
  3. atom의 값이 변경되면, getPokemon selector가 변경 된 atom의 값을 가져와서 호출 후 json으로 변환 해서 return

대략적인 순서는 이러하며, 그리고 selector를 사용하면 값을 캐시해두는 데, 현재 요청이 이전에 요청한 적 있는 데이터라면 캐시된 값을 바로 사용하는 것이 장점이고, redux처럼 라이브러리 자체가 무겁지 않아서 그 점도 장점인 것 같아요

그리고 recoil은 상단에서 언급한것 처럼 hook과 굉장히 닮아 있어서 사용하기가 무척 편했(?)어요. 하지만 redux처럼 devtool이 없기 때문에 디버깅 측면에서는 다소 불편한 점이 있었지만 페이스북에서 직접 제시한 솔루션인만큼 많은 발전이 생길꺼라 기대하며 사이드 프로젝트에 적용해보기로 결심했어요

분명 redux도 recoil도 상태 관리하는 데에 있어서 아주 좋은 라이브러리 들이라서 어느 것을 선택해도 괜찮을 것 같아요

profile
조금씩 나아지기

0개의 댓글