22sook00 logo
SookDev

Recoil

tag
library
date
Nov 10, 2023

01_Recoil 사용 이유

기존 프로젝트에서 사용했던 복잡한 ContextAPI 대신 간단한 Recoil 로 전환하기
리액트에서 기본으로 제공하는 ContextAPI 를 사용하려면 하나의 상태관리 마다 Provider 로 감싸줘야 한다.
<AuthContext.Provider value={{ user: currentUser }}> <CartContext.Provider> <DesignContext.Provider> <ConfigContext.Provider> ... {children} </ConfigContext.Provider> </DesignContext.Provider> </CartContext.Provider> </AuthContext.Provider>
또한 contextAPI 같은 경우 계속 상태가 업데이트 되면서 하위에 있는 자식들도 다 렌더링 하는
성능적으로 안좋은 성질이 있어, useCallback이나 memo 로 관리 해줘야 한다.
반면 리코일은 컴포넌트 사이에서 데이터를 공유하는 상태관리 툴로,
Atom 이라는 단위로 상태를 정의하고 recoilProvider 한번만 감싸주면 되기 때문에 사용성 측면에서 매우 간편하다.
✳️ 정리
  • 간편한 상태관리
  • 최적화된 리렌더링 : 내부적으로 최적화 되어있어 필요한 경우에만 리렌더링
  • 복잡한 어플리케이션에 적합
 

02_Recoil 기능

Atom, Selector

Atom

앱의 상태를 정의하는 단위로, atom 함수를 통해 생성
const textState = atom({ key: 'textState', // unique ID (with respect to other atoms/selectors) default: '', // default value (aka initial value) });

Selector

파생된 상태를 만들어내는 함수로 다른 atom 이나 selector 로부터 계산
const charCountState = selector({ key: 'charCountState', // unique ID (with respect to other atoms/selectors) get: ({get}) => { const text = get(textState); return text.length; }, });
useRecoilValue() 훅을 사용해서 charCountState 값을 읽을 수 있다.
function CharacterCount() { const count = useRecoilValue(charCountState); return <>Character Count: {count}</>; }
 

Recoil 훅

UseRecoilState
Atom 값을 읽고 업데이트하는데 사용되는 훅.
useState 와 비슷한 기능을 한다고 생각하면 된다.
import {atom,useRecoilState,RecoilRoot} from 'recoil' const countState = atom({ key:'countState', default: 0 }) const Counter = () => { const [count,setCount] = useRecoilState(countState) return ( <div> <p>Count : {count}</p> <button onClick={()=>setCount(count+1)}>+</button> <button onClick={()=>setCount(count-1)}>-</button> </div> ) }
useRecoilValue
만약 값만 읽고 싶다면 useRecoilValue 를 사용하면 된다. (Read only 의 느낌.)단순 Atom 값을 읽는데 사용되며 상태 업데이트를 트리거 하지 않는다.
import {selector,useRecoilValue,RecoilRoot} from 'recoil' const userNameState = atom({ key:'userNameState', get:({get})=>{ const : user = get(userState); return user ? user.name : 'Guest' } }) const UserInfo = () => { const userName = useRecoilValue(userNameState) return ( <div> <p>Welcome, {userName}</p> </div> ) }
useSetRecoilState
Atom 값을 업데이트하는 setter 함수 반환
import {atom,useSetRecoilState,RecoilRoot} from 'recoil' const countState = atom({ key:'countState', default: 0 }) const Counter = () => { const setCount = useSetRecoilState(countState) return ( <div> <button onClick={()=>setCount((prev)=>prev+1)}>+</button> </div> ) }
useResetRecoilState
Atom 값을 초기값으로 리셋하는 함수를 반환
import {atom,useSetRecoilState,RecoilRoot} from 'recoil' const countState = atom({ key:'countState', default: 0 }) const Counter = () => { const re setCount = useResetRecoilState(countState) return ( <div> <button onClick={resetCount}>RESET!</button> </div> ) }
 
dangerouslyAllowMutability
기본적으로 상태의 불변성을 유지하기 때문에 Recoil 에 등록된 상태가 변경 되었을때 감지하고 해당 컴포넌트를 다시 렌더링 하도록 한다.
 

02_Recoil 예제

지도 구현을 통한 recoil 사용
Atom 값 설계
  • 지도 기본상태를 저장하기 위한 mapState
  • 현재 선택한 맛집 상태를 저장하기 위한 currentStoreState
  • 현재 위치 (Map,Marker) 및 zoom 상태를 저장하기 위한 locationState
  • 검색 상태를 저장하기 위한 searchState
 

03_Recoil 중복 atom key 이슈

Duplicate atom key “map”. This is a FATAL ERROR in production. But it is safe to ignore this warning if it occurred because of hot module replacement.

이슈 원인

next dev 를 사용하여 프로젝트 실행 시 , 해당 에러가 콘솔에 출력된다.
이유 : Next.js 개발 중 파일이 변경되면 핫 리로드 때문에 다시 빌드가 된다.
이 과정에서 atom 으로 만든 state 가 재선언이 된다.
재선언 되는 과정에서 이미 key 로 선언된 값을 다시 사용해서 문제가 발생하게 된다.
 

해결방법

  1. 해당 이슈는 개발단에서만 이루어지기 때문에 .env 환경변수 파일에 다음 코드 추가하고, 배포 후에는 해당값이 필요 없다.
      • RECOIL_DUPLICATE_ATOM_KEY_CHECKING_ENABLED=false
  1. 문제가 되는 key 값에 uuid() 라이브러리 활용하여 난수 추가