여행가는개발자

Recoil 본문

React

Recoil

kimsoonil 2023. 10. 10. 10:28
728x90
반응형

Recoil은 페이스북이 2020년 5월에 소개한 React 전용으로 나온 상태 관리 라이브러리다.

Recoil 공식 홈페이지

https://recoiljs.org/ko/docs/introduction/getting-started/

 

Recoil 시작하기 | Recoil

React 애플리케이션 생성하기

recoiljs.org

 

Recoil을 통해 전역 상태를 관리하면 코드가 굉장히 간결해진다.

context API는 전역 상태를 전달할 때 객체 형태의 value를 사용한다. 따라서 객체 안의 값이 하나라도 변경되면 provider로 감싼 모든 하위 컴포넌트들이 리렌더링한다는 단점이 있다.
Recoil의 경우 각각의 전역 상태에 대한 atom이 생성되고 해당 상태를 구독하는 구성 요소만 리렌더링 된다. 따라서 불필요한 리렌더링을 방지할 수 있다.

 

기존에 redux - saga나 redux - thunkfmf 사용한것보다 간편하고 쉬웠으며 개인적으로는 Zustand 보다 편리하다고 느꼈다.

이전 전역 상태관리 라이브러리 문제점

Redux의 경우 Flux 패턴을 사용하여 안정적으로 상태 관리가 가능하다. 그럼에도 Redux에 대한 불만이 꾸준히 나오는 데에는 이유가 있을 것이다.

  • React 전용 라이브러리가 아니다.
  • 초기 세팅(boilerplate)이 요구된다.
  • 비동기 데이터를 사용하려면 미들웨어 설치 등 추가적인 라이브러리 설치가 필요하다.

Recoil은 이러한 단점을 보완한 라이브러리이다.

Recoil 장점

  • Recoil은 전역 상태 관리를 컴포넌트 내부의 상태 관리처럼 간단한 get/set 인터페이스로 사용할 수 있도록 boilerplate-free API를 제공한다.
  • Concurrent Mode를 비롯한 다양한 React 기능들과 호환이 가능하다.
  • 상태 정의에 대한 코드 분할이 가능하다.
  • 전역 상태를 사용하고 있는 컴포넌트는 수정 없이 변경된 상태로 교체할 수 있다.
  • 컴포넌트 수정 없이 동기식/비동기식 전환이 가능하다.
  • 전역 상태의 경우 애플리케이션이 변경되더라도 그대로 유지된다.

Concurrent Mode (동시성 모드)
흐름이 여러개가 존재하는 경우를 의미한다. 즉, 리액트에서 알아서 렌더링 동작의 우선순위를 정하여 적절한 때에 렌더링을 해주는 것이다.

 

주요 개념

data-flow graph

상태 데이터가 atoms -> selectors -> 컴포넌트 순서로 흐르는 것을 말한다.

Atoms

Atoms는 상태 단위이며, 업데이트와 참조가 가능하다.
atom이 업데이트되면 각각의 컴포넌트들은 새로운 값을 반영하여 리렌더링 된다.
atoms는 컴포넌트 내부의 상태 대신 사용될 수 있다.
동일한 atom이 여러 컴포넌트에서 사용되는 경우 그 컴포넌트들은 상태를 공유한다.

Atoms는 atom 함수를 사용해 생성한다.
atom의 키 값은 전역적으로 고유해야 한다.

const fontSizeState = atom({
  key: 'fontSizeState', // 고유한 키 값
  default: 14,
});

등록된 전역 상태를 사용하기 위해서는 useRecoilState(키값)를 사용하면 된다. useState()와 사용법이 비슷하나 차이점이 있다면 내부 상태가 아닌 전역 상태라는 것이다.

function FontButton() {
  const [fontSize, setFontSize] = useRecoilState(fontSizeState);
  return (
    <button onClick={() => setFontSize((size) => size + 1)} style={{fontSize}}>
      Click to Enlarge
    </button>
  );
}

버튼을 클릭하면 폰트 사이즈가 1 증가하며, 해당 전역 상태를 사용하고 있는 다른 컴포넌트에서도 같이 변경된다.

function Text() {
  const [fontSize, setFontSize] = useRecoilState(fontSizeState);
  return <p style={{fontSize}}>This text will increase in size too.</p>;
}

 

selectors

selector는 전역 상태를 기반으로 어떤 계산을 통해 새로운 값을 내뱉는 순수함수다.
atom이나 다른 selector를 통해 입력을 받을 수 있다.
상위의 atom이나 다른 selector가 업데이트되면 하위의 selector 함수도 다시 실행된다.
컴포넌트들은 위에서 atom을 통해 전역 값을 사용했던 것처럼 selector를 사용할 수 있다.

기본적이고 최소한의 상태들은 atom에 저장해두고, selector에 명시한 함수를 통해 상태 값을 변경하면 된다. 따라서 이전 상태 관리 라이브러리들이 기존 상태 값을 보존하기 위해 했던 짓을 안 해도 된다.

컴포넌트의 관점으로 봤을 때 atom과 selector는 동일한 인터페이스를 가지므로 대체하여 사용할 수 있는 것이다.

get은 상태를 계산할 함수가 담겨져 있다. get으로 전달되는 매개변수를 통해 atom이나 다른 selector에 접근할 수 있다. (종속 관계 생성)

const fontSizeLabelState = selector({
  key: 'fontSizeLabelState', // 고유한 키 값
  get: ({get}) => {
    const fontSize = get(fontSizeState); 
    const unit = 'px';

    return `${fontSize}${unit}`;
  },
});

위 예시에서 selector는 fontSizeState라는 atom에 의존성을 갖는다.

function FontButton() {
  const [fontSize, setFontSize] = useRecoilState(fontSizeState);
  const fontSizeLabel = useRecoilValue(fontSizeLabelState);

  return (
    <>
      <div>Current font size: ${fontSizeLabel}</div>

      <button onClick={setFontSize(fontSize + 1)} style={{fontSize}}>
        Click to Enlarge
      </button>
    </>
  );
}

selector는 useRecoilValue(키값)을 통해 사용할 수 있다.
버튼을 클릭하면 폰트 사이즈도 증가하는 동시에 fontSizeLabel도 업데이트 되어 컴포넌트에 반영된다.

 

사용하기

Recoil은 webpack, Rollup과 같은 번들러와도 문제없이 호환된다.

설치

npm 설치

npm install recoil

or yarn  설치

yarn add recoil

RecoilRoot 배치

recoil 상태를 사용하는 컴포넌트는 부모 트리 어딘가에 나타나는 RecoilRoot 가 필요하다. 루트 컴포넌트가 RecoilRoot를 넣기에 가장 좋은 장소다. (Recoil 본문 발췌)

import { RecoilRoot } from 'recoil';

export default function App(props: AppProps) {
    const { Component } = props;
  
    return (
        <RecoilRoot>
          <Component {...pageProps} />
        </RecoilRoot>
    );
}

저는 현재 next.js 프레임워크 환경에서 진행하고 있기 때문에 _app.tsx에서 RecoilRoot 컴포넌트를 감싸줬습니다. 

react 환경에서는 app.js에서 감싸주면 됩니다.

Atom 설정

Atom 상태(state)의 일부를 나타낸다. Atoms는 어떤 컴포넌트에서나 읽고 쓸 수 있다. atom의 값을 읽는 컴포넌트들은 암묵적으로 atom을 구독한다. 그래서 atom에 어떤 변화가 있으면 그 atom을 구독하는 모든 컴포넌트들이 재 렌더링 되는 결과가 발생할 것이다. (Recoil 본문 발췌)

import { atom } from 'recoil';

export interface IContentTypes {
    name: string;
    status: boolean;
    message: string;
}

//recoil state 생성
export const contentState = atom<IContentTypes>({
    key: 'content',
    default: {
        name: 'test',
        status: false,
        message: ''
    }
});

이런식으로 별도 state.ts 파일을 생성하여 관리할 상태를 atom을 사용하여 설정작업을 진행해준다.

(typescript 환경이 아닌경우 타입 내용 빼고 사용)

 

 

위 작업들로 이제 recoil 상태관리를 사용할 준비는 벌써 끝났다. redux와 비교하면 너무도 단순하다.

 

redux : 스토어 생성 > 리듀서 생성 > 액션 설정 > 디스패치(액션) 호출 

recoil : 스토어 생성 > 호출 

 

사용방식에 따라 단계는 좀 바뀔 수 있지만 간단하게는 저런 차이다.

 

사용하기

import { useState, useEffect } from 'react';

//state, recoil import
import { contentState } from './state';
import { useSetRecoilState, useRecolValue } from 'recoil';

export default function sample() {
	
    const [reqContent, setReqContent] = useState({
        name: "sample",
        status: true,
        message: "테스트 메세지"
    });
    
    //recoil 사용 선언부
    const setContent = useSetRecoilState(contentState);
    const content = useRecolValue(contentState);
  
    useEffect(()=>{
        //recoil setting 
        setContent(reqContent);
    },[])
    
    return (
    	{content && (
        	<div>
            	{content.name},
                {content.status},
                {content.message}
            </div
        )}
    );
}

1. 사용할 recoil state를 import

2. 사용할 recoil 기능 import

    ( useRecoilState : react의 useState랑 동일한 기능이라고 생각하면 된다.

      useSetRecoilState : useState에서 setter만 있는것

      useRecolValue : useState에서 value만 있는것

      useResetRecoilState : 기본값으로 초기화 시키는 기능)

3. 위 기능을 사용하여 기능 구현

 

728x90
반응형

'React' 카테고리의 다른 글

에디터 사용기 (tinymce-react)  (0) 2023.11.07
Recoil을 활용하여 팝업 공통화하기  (0) 2023.10.12
Json-server  (0) 2023.09.15
Typescript vs Javascript  (0) 2023.09.15
Yup  (0) 2023.09.15