Recoil 사용기

김태영·2023년 6월 16일
0

Recoil은 redux와 마찬가지로 상태관리를 위한 JS 상태관리 툴이다.

context API 기반으로 구현되어 있으며, 함수형 컴포넌트에서만 사용할 수 있음

Recoil 설치

npm i recoil
// or
yarn add recoil

RecoilRoot

리코일 state를 사용하는 컴포넌트들은 <RecoilRoot>를 필요로 한다. <RecoilRoot>를 사용하는 가장 좋은곳은 root component이다.

import React from 'react';
import {
  RecoilRoot,
  atom,
  selector,
  useRecoilState,
  useRecoilValue,
} from 'recoil';    

function App() {
  return (
    <RecoilRoot>    // 이렇게 감싸주어야한다.
      <CharacterCounter />
    </RecoilRoot>
  );
}

Atom

아톰은 상태를 말하며 어떠한 컴포넌트에서 씌여지고 읽혀질 수 있다. 아톰의 밸류를 읽는 컴포넌트들은 암묵적으로 그 아톰에게 참고(subscribe(구독이라고 해도 될거 같다.) )되어지고 있다. 그래서 아톰의 업데이트는 이 아톰을 참고하고 있는 컴포넌트를 리-렌더 시킴

atom.png

const textState = atom({
  key: 'textState', // unique ID 
  default: '', // default value 
});

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

아톰 상태값을 필요로 하는 컴포넌트에서 , 원할 때 마다 꺼내쓸 수 있다.

useRecoilState는 useState처럼 사용이 가능한데,

atom선언한 값을 useReocilState로 사용했을 때 전역상태관리가 가능함

Selector

Selector는 전역 상태 값을 기반으로 어떤 계산을 통해 파생된 상태(derived state)를 반환하는 순수함수

get함수만 제공되면 Selector는 읽기만 가능한 RecoilValueReadOnly 객체를 반환. set 함수 또한 제공되며 (optional) Selector는 쓰기 가능한 RecoilState 객체를 반환한다.

get 매개변수를 이용하여 atom이나 다른 selector를 참조할 수 있다

// selectors.js
import { selector } from "recoil";
import { countState } from "./atoms";

export const countNextState = selector({
  key: "counterNextState",
  get: ({ get }) => {
    return get(countState) + 1;
  }
});

// CounterInfo.js
import React from "react";
import { useRecoilValue } from "recoil";
import { countNextState } from "../states/selectors";

export const CounterInfo = () => {
  const nextCount = useRecoilValue(countNextState);
  return <p>the next number is {nextCount}</p>;
};

useRecoilValue() hook을 사용해서 charCountState 값을 읽을 수 있다

사용방법은 앞서 설명한 useRecoilState()와 거의 동일하지만 setter함수는 반환하지 않음 그러므로 setter 함수 없이 state를 읽기만 하는 컴포넌트에서 유용함.

**useRecoilValueLoadable()을 이용한 비동기 처리**

useRecoilValueLoadable() hook을 사용하여 렌더링 중 상태(status)를 확인할 수 있다.

앞서 사용했던 useRecoilValue()는 Error를 던지거나 Promise를 반환하지 않는다.

useRecoilValueLoadable()는 Loadable 객체를 반환합니다. 이를 이용해 비동기 처리를 할 수 있음.

Lodable 객체

  • state: slector의 상태를 반환. hasValuehasErrorloading 값 중 하나를 String으로 반환함.
  • contents: Loadable이 나타내는 값.
    • state가 hasValue이면 실제 값.
    • state가 hasError이면 throw된 Error객체.
    • state가 loading이면 Promise.

이번 프로젝트를 하면서 적용했음.

 // atom/News.tsx //
export const getNewsSelector = selector<MainNewsList[]>({
  key: "getNewsSelector",
  get: async ({ get }) => {
    const response = await api.get(`/news`);
    return response.data;
  },
});
 // NewsDetailView.tsx //
const NewsDetailView = () => {
  const getNews = useRecoilValueLoadable(getNewsSelector);
  const Content = styled.div`
    min-height: 100vh;
    margin: 0 auto;
    color: black;
  `;

  // const selectNewsLoadable = useRecoilValueLoadable(selectNews);

  let LoadablegetNewsList: MainNewsList[] = [];
  switch (getNews.state) {
    case "hasValue":
      LoadablegetNewsList = getNews.contents;
      break;
  }

  return (
    <>
      <DetailTopBar title="뉴스" />
      <Content className="max-w-md bg-stone-100 p-3 pb-8">
        <NewsBrandCarouselMenu />
        <div className="max-h-screen overflow-auto scrollbar-hide">
          <div>
            {getNews.state === "loading" ? (
              <LoadingBar />
            ) : (
              LoadablegetNewsList.map((item: any, idx: number) => (
                <div
                  className="border-t flex text-left py-3  border-stone-300"
                  key={idx}
                >
                  <img
                    src={item.imagePath}
                    className={`${item.imagePath ? "w-28 mr-2" : "pl-1"}`}
                  />
                  <a href={`${item.url}`}>
                    <span>{item.subject}</span>
                  </a>
                </div>
              ))
            )}
          </div>
        </div>
      </Content>
    </>
  );
};
export default NewsDetailView;

로딩 상태일때는 로딩바 컴포넌트로 로딩처리,

로딩 완료되고 값을 성공적으로 받아왔을 때 값을 사용해 컴포넌트 리턴

profile
구렁이

0개의 댓글