[멋사] 8주차 사전과제 - Recoil (vs Context API)

·2023년 5월 26일
0

likelion

목록 보기
11/14
  1. 전역 상태에 대해 공부 + 정리
  2. Recoil 공식 문서 보면서 공부 + 정리
  3. context와 recoil 비교

전역상태 관리

프론트엔드에서의 상태(State)란?

어플리케이션의 화면에 영향을 끼치는 자바스크립트 객체

보통은 이를 통칭해서 "변화하는 데이터"라고 일컫는다.
상태 중에서도 전역상태란 프로젝트 전체에 영향을 끼치는 상태를 의미한다.

일반적으로 리액트에서 데이터는 부모로부터 props를 통해 전달된다. 그러나 컴포넌트를 나누다보면 상위 컴포넌트의 state를 props를 통해 전달하고자 하는 컴포넌트로 전달하기 위해 그 사이는 props를 전달하는 용도로만 쓰이는 props drilling 현상이 나타난다.

이와 같이 props로만 계속해서 자식 컴포넌트에 데이터를 전달한다면 코드가 지저분해지고, 유지보수 또한 힘들어진다.
또한 state 변경시 props 전달 과정에서 불필요하게 관여된 컴포넌트들도 리렌더링이 발생하므로 웹성능에 악영향을 끼칠 수 있다.

이를 해결하기 위해서 우리는 전역상태를 관리해주는 도구를 사용해서 상태관리를 해주어야 한다.
(전역상태 관리 도구로는 대표적으로 Context API, Recoil, Redux 등이 있다.)

Recoil

페이스북에서 만든 React 프로젝트를 위한 전역 상태관리 라이브러리

Recoil 설치

$ yarn add recoil

RecoilRoot

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

App.js

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

function App() {
  return (
    <RecoilRoot>
      <Children />
    </RecoilRoot>
  );
}

Atom

컴포넌트끼리 공유 가능한 가장 작은 단위의 state

아톰은 상태의 단위이며 어떠한 컴포넌트에서나 읽고 쓸 수 있다. 아톰 값을 읽는 컴포넌트들은 암묵적으로 그 아톰을 구독한다. 아톰이 업데이트되면, 해당 아톰을 구독하고 있던 모든 컴포넌트들이 새로운 값으로 리렌더링된다.
또, 여러 컴포넌트에서 같은 아톰을 구독하고 있으면 그 컴포넌트들은 상태를 동일하게 공유한다.

import { atom } from 'recoil';

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

아톰마다 고유한 값 key를 가지며, default에 기본값(초기값)을 할당해준다.

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를 이용하여 state를 전역으로 받아올 수 있고 변경할 수 있다. (useState와 유사)

Selector

Atom 또는 다른 Selector로 구성한 순수함수

셀렉터는 파생된 상태(derived state)의 일부를 나타낸다.
파생된 상태란 상태의 변화로, 주어진 상태를 변경시키는 순수함수에 의해 전달된 상태의 결과물이라고 할 수 있다.
구독 중인 아톰 또는 셀렉터가 업데이트되면 셀렉터도 업데이트 된다.

import { atom, selector } from 'recoil';

export const sampleState = atom({
  key: "sampleState",
  default: 0,
});

export const sampleSelector = selector({
  key: "sampleSelector",
  get: ({ get }) => get(sampleState) * 2,
  set: ({ set }, newValue) => set(sampleState, newValue / 2),
});

get을 이용하여 다른 아톰도 구독할 수 있다. -> 즉, 해당 아톰이 변하면 셀렉터도 변한다.
get을 통해 전처리한 atom의 값을 받아오고, set을 통해 atom값을 후처리할 수 있다.
셀렉터에서 getter, setter를 사용해 전처리 및 후처리한 atom을 사용할 수 있다.

사용 방법은 atom과 마찬가지로 useRecoilState를 사용한다.

useRecoilState

useState와 유사한 기능이다. 그러나 선언을 하는 게 아닌 전역적으로 설정된 atom을 가져오는 역할이다.

const [sample,setSample] = useRecoilState(sampleState);

useRecoilValue

아톰을 조회할 때만 사용한다.

const sample = useRecoilValue(atom);

useSetRecoilState

값을 불러오지 않고 상태만을 설정할 때 사용한다. 아톰을 변경시킬 수 있다.

const setSample = useSetRecoilState(atom);

useResetRecoilState

아톰 값을 디폴트값(초기값)으로 변경시킨다.

종합예시!

Atom.js

import { atom, selector } from 'recoil';

const tempFahrenheit = atom({
	key: 'tempFahrenheit',
	default: 32,
});

const tempCelsius = selector({
	key: 'tempCelsius',
	get: ({get}) => ((get(tempFahrenheit) - 32) * 5) / 9,
	set: ({set}, newValue) => set(tempFahrenheit, (newValue * 9) / 5 + 32),
});

export {tempFahrenheit, tempCelsius}

TempCelsius.js

import {atom, selector, useRecoilState, useRecoilValue} from 'recoil';
import {tempFahrenheit, tempCelsius} from "./Atoms.js"

function TempCelsius() {
  const [tempC, setTempC] = useRecoilState(tempCelsius);
  const [tempF, setTempF] = useRecoilState(tempFahrenheit);
  const originTempC = useRecoilValue(tempCelsius); 
  const originTempF = useRecoilValue(tempFahrenheit); 

  const addTenCelsius = () => setTempC(tempC + 10);
  const addTenFahrenheit = () => setTempF(tempF + 10);


  return (
    <div>
      Temp (Celsius): {tempC}
      <br />
      Temp (Fahrenheit): {tempF}
      <br />
      <button onClick={addTenCelsius}>Add 10 Celsius</button>
      <br />
      <button onClick={addTenFahrenheit}>Add 10 Fahrenheit</button>
      <br />
      <p>{`originC : ${originTempC} / originB : ${originTempF}`}</p>
    </div>
  );
}

export default TempCelsius;

결과

Context API vs Recoil

속성 정의

  • Recoil: 전역으로 설정되기 때문에 키 값을 가지며 그 키 값은 유일해야 한다.
  • Context: createContext를 사용하여 해당 Context를 생성한다.

Hook 사용

  • Recoil: useRecoilState, useSetRecoilState, useValueRecoilState
  • Context: useContext

사용 방법

  • Recoil: Hook을 사용하여 다른 컴포넌트에서 state 접근
  • Context: Provider를 사용하여 다른 컴포넌트에 제공할 value 선언
    (createContext를 사용했을 때 선언한 값과 value 타입이 일치해야함)

특징

  • Recoil: 변경된 아톰의 상태를 공유하고 있는 컴포넌트만 리렌더링된다.
  • Context: Provider 하위의 모든 consumer들이 Provider 속성이 변경될 때마다 다시 렌더링된다.
    (Provider 값이 배열 혹은 객체인 경우, 구조가 조금이라도 변경되면 그 Context를 구독하고 있는 하위의 모든 것들이 다시 렌더링된다.)

0개의 댓글