recoil 개념 잡기

saebyeol·2022년 4월 11일
0

이번 큐시즘 프로젝트를 준비하며 상태관리 라이브러리로 고민이 많았다.
이전까지는 동아리 플젝을 할 때 따로 상태관리 라이브러리를 쓰지 않고 그냥 리액트의 훅들을 활용하며 끙끙대왔다 ㅋㅋ.. 그래서 항상 리덕스를 배워야겠다는 생각은 하고있었다..

이번 플젝에서 리덕스를 해야하나 고민 중이었는데 팀원 언니가 요즘 리코일도 떠오르고 있다고 알려주었고 찾아보니 리덕스보다 간단하고 리액트에 최적화 되어있는 라이브러리였다.
플젝 개발을 시작하기까지 2주도 남지않아 빠르게 배울 라이브러리가 필요했고
복잡하고 어려운 리덕스보다 간단히 사용가능한 리코일이 더 낫겠다는 생각이 들었다.

이번 플젝은 리코일로 상태관리를 하고 종강 후 방학때 천천히 리덕스를 배워보는것으로 하겠다..!

리코일 Todo실습을 하기 전 개념부터 잡고 코드를 짜보려고 한다.

1️⃣ Recoil의 기본, Atom

atom은 기존의 redux에서 쓰이는 store와 유사한 개념으로, 상태의 단위이다.
atom이 업뎃되면 해당 atom을 구독하고 있던 모든 컴포넌트들의 state가 새로운 값으로 리렌더 된다.

state.js

export const cookieState = atom({
  key: 'cookieState',
  default: []
});

atom은 다음과 같이 작성한다. key로 고유한 atom을 구분하고, default에 기본으로 atom에 저장되는 값을 지정할 수 있습니다.

Cookie.js

import React from 'react'
import { cookieState } from '../../state';
import { useRecoilState } from 'recoil';

const Cookie = () => {
	const [cookies, setCookies] = useRecoilState(cookieState);
  
  return(
    <div>
        {cookie.map(cookie => (
          <Card
            cookies={cookie}
            key={cookie.id}
            idx={cookie.id}
           />
       ))}
      </div>
  );
}
export default Cookie;

atom에서 정의한 state를 사용하는 것도 위와 같이 간단하다.
useRecoilState를 사용해 state를 가져와 사용할 수 있다.

2️⃣ useRecoilState

useRecoilState()의 역할을 반으로 쪼개 useRecoilValue() 과 useSetRecoilState()로 나눌 수 있다.

import { useRecoilValue, useSetRecoilState } from 'recoil';
import { cookieState } from '../../state';

const cookies = useRecoilValue(cookieState);
const setCookies = useSetRecoilState(cookieState);

3️⃣ Selector와 비동기처리 (Suspense, Loadable)

atom만 가지고 api통신과 같은 비동기처리를 하려면 다음과 같이 코드를 작성해야한다.

useEffect(()=>{
  (async ()=>{
    const { data }= await getApi.getCookies();
    setCookie(data);
  })();
}, [setCookie]);

이런식으로 비동기 처리를 해도 무방하지만 selector를 사용하면 성능적으로 더욱 유리하다.

state.js

export const cookieState = atom({
  key: 'cookieState',
  default: []
});

export const getCookieSelector = selector({
  key: "cookie/get",
  get: async ({ get }) => {
    try{
      const { data } = await client.get('/cookies');    
      return data.data;
    } catch (err) {
    	throw err;
    }
  },
  set: ({set}, newValue)=> {
    set(cookieState, newValue)
  }
});
  • key : selector를 구분할 수 있는 키.
  • get : 파생된 상태를 리턴하는 곳으로 위의 예제에서는 api에서 받아온 데이터를 리턴하였다.
const cookie = useRecoilValue(selector)

위와 같이 리턴값을 읽을 수 있다.

  • get : writeable 한 state 값을 변경할 수 있는 함수를 return 하는 곳. 자기 자신 selector를 set 하려고 하면, 무한루프가 돌게 되니 반드시 다른 selector와 atom을 set 하는 로직을 구성해야한다. 또한 애초에 selector는 read-only 한 return 값(RecoilValue)만 가지기 때문에 set으로는 writeable 한 atom 의 RecoilState 만 설정할 수 있다.
const [cookie, setCookie] = useRecoilState(cookieState);

이런식으로 set을 사용할 수 있다.

위의 selector를 사용하면

import { getCookieSeletor } from '../../reocil';

const Cookies = () =>{
  const [cookie, setCookie] = useRecoilState(getCookieSelector);
}

return (
  <>
   (<div>
        {cookie.map(cookie => (
          <Card
            cookies={cookie}
            key={cookie.id}
            idx={cookie.id}
           />
       ))}
      </div>)
  </>
});

export default Cookies;

이렇게 코드를 작성하면 된다.

4️⃣ 비동기 상태 처리

아직 데이터가 도착하기 전 즉, 비동기 처리를 하고 있을 경우 보여줄 fallback 페이지가 필요하다.

  • 방법 1. React.Suspense
import React,{ Suspense } from 'react';
import { Cookies } from '../components';

const App = () => {
  return(
    <RootRecoil>
      <Suspense fallback={<div>Loading...</div>}>
        <Cookies />
      </Suspense>
    </RootRecoil>
   );
}

export default App;
  • 방법2. Recoil의 Loadable
import { getCookieSeletor } from '../../reocil';
import { useRecoilState, useRecoilValueLoadable } from 'recoil';

const Cookies = () => {
  const cookieLoadable = useRecoilValueLoadable(getCookieSelector);

  switch(cookieLoadable.state){
    case 'hasValue':
      return (
        <>
          (<div>
    	    {cookieLoadable.contents.map(cookie =>(
              <Card
                cookies={cookie}
                key={cookie.id}
                idx={cookie.id}
               />
            ))}
	     </div>)
	  </>
	});
     case 'loading':
  	return <Loading />;
     case 'hasError':
     	throw cookieLoadable.contents;
}


export default Cookies;

5️⃣ selector를 통한 성능 개선 - 캐싱

selector는 캐싱을 통해 한번 들어왔던 적이 있던 값을 기억하고 있는다. 따라서 같은 응답을 보내는 api 를 요청하는경우 추가적인 요청을 하지 않아 성능이 개선된다.

동적인 url api를 요청하는 경우에는 (즉, 파라미터로 값을 받아와 이를 url에 적용해야하는 경우) selectorFamily를 사용하여 구현할 수 있다.

출처: https://velog.io/@juno7803/Recoil-Recoil-200-%ED%99%9C%EC%9A%A9%ED%95%98%EA%B8%B0

profile
프론트엔드 개발자

0개의 댓글