React 상태 관련 라이브러리 - Recoil(1)

jiny·2022년 9월 1일
0

React

목록 보기
3/11
post-thumbnail
post-custom-banner

목차

1. react의 상태 관리
2. props를 이용한 상태 관리의 문제점
3. recoil
4. Atoms

React의 상태관리

단방향 데이터 바인딩

  • 리액트의 데이터 바인딩은 부모 컴포넌트에서 자식 컴포넌트로 흐르는 단방향 데이터 바인딩을 지원
  • 반대로 자식 -> 부모 컴포넌트로는 데이터 바인딩이 되지 않음

flux 아키텍처

  • 좀 더 자세하게 설명하기 위해 flux 아키텍처를 설명하려고 한다.
  • 현재 React에서는 Facebook이 고안한 flux 아키텍처를 주로 사용

과정

  1. View에서 어떤 액션(이벤트 or 인터렉션)이 발생
  2. Dispatcher가 액션을 캐치하여 store로 이동 시킴
  3. Store는 view에게 데이터를 전달
  4. mvc는 view와 model이 서로 상호작용 할 수 있지만 flux는 오로지 단방향으로 흐르는 특징을 지님

HomeComponent.tsx

import { CommonComponent, CommonSubComponent, Container, SubTitle, Title } from "src/components/commons/Commons"
import Temperature from "src/components/home/atoms/Temperature";
import useWeatherData from "src/hooks/useWeatherData";


export default function HomeComponent () : React.ReactElement {
    const {data, temp, weather} = useWeatherData();
    return (
        <Container>
        	<Temperature temp={temp}></Temperature>
        </Container>
    )
}

Temperature.tsx

import styled from "styled-components"

export const WeatherContent = styled.div`
    display: flex;
    justify-content: center;
    font-size: 45px;
`

interface TemperatureProps {
    temp : number;
}

export default function Temperature ({ temp } : TemperatureProps) : React.ReactElement {
    return(
        <WeatherContent>{temp + "℃"}</WeatherContent>
    )
}
  • 이런 형태로 부모가 내려준 데이터(temp)를 자식이 받아서 사용하는 것이 리액트의 일반적인 상태 관리 패턴

props를 이용한 상태 관리의 문제점

  • 리액트로 컴포넌트 단위로 어플리케이션을 구성
  • 어플리케이션의 규모가 커질 수록 당연히 컴포넌트 트리의 구조는 복잡해짐

  • 복잡한 구조의 컴포넌트 트리에서 상태 관리시 props drilling 발생

    props drilling - props를 오로지 하위 컴포넌트로 전달하는 용도로만 쓰이는 컴포넌트들을 거치면서 React Component 트리의 한 부분에서 다른 부분으로 데이터를 전달하는 과정

props drilling의 문제점

  • 부모 -> 자식 컴포넌트로 데이터 이동 시 수 많은 자식들을 거쳐가게 되면 데이터 추적이 어려워지게 됨
  • 그럴 경우 리팩토링 시 유지보수 측면에서 매우 좋지 않음

Recoil

Recoil?

A state management library for React

  • Recoil은 facebook에서 출시한 상태 관리 라이브러리
  • React를 위한 편리한 이점들을 가진 상태 관리 관련 함수를 제공

  • Recoil의 전체적인 구성을 그림으로 나타낸 것
  • 1~6까지의 atoms들은 모든 컴포넌트에서 구독할 수 있으며, 만약 atoms의 상태가 변경되면 모든 컴포넌트들은 업데이트 되며 재 렌더링이 발생

    더 이상 props drilling이 발생하지 않으며, atoms에 등록된 상태는 모든 컴포넌트들이 구독할 수 있는 전역 상태가 되는 것

Recoil의 구성요소

  1. atoms (공유 상태)
  2. selector (순수 함수)

Recoil의 장점

1. 적은 코드양, 쉬운 러닝 커브

  • Recoil은 상태 하나를 atom으로 정의하고, 그것을 구독하는 패턴
  • 또한 이런 atom 들로부터 파생된 상태를 selector로 선언
  • 이 selector 들은 구독한 atom이 변화하면 자동으로 변화
  • 페이스북에서 만든 만큼 훅을 사용해보았다면 쉽게 배울 수 있음 -> useState와 매우 유사한 형태를 지님

2. 렌더링 최적화

  • Context API(useContext())에서는 상태 변경 시 구독한 하위 컴포넌트들이 모두 리렌더링 되는 문제가 있었음
  • Recoil은 atom, selector를 구독하면 구독한 컴포넌트만 리렌더링이 발생

3. 간편한 비동기 처리

  • Recoil에서 자체적으로 selector에서 지원
  • 에러처리도 리액트의 suspense를 사용할 수 있음
  • useRecoilValueLoadable을 사용한다면, hasValue, loading, hasError 상태를 제공해서, 로딩 완료 시, 로딩 시, 에러 시 분기 처리가 손쉽게 가능

Recoil install & settings

install

npm i recoil

index.tsx

import React from 'react';
import ReactDOM from 'react-dom/client';
import { RecoilRoot } from 'recoil';
import App from './App';

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
root.render(
  <React.StrictMode>
    <RecoilRoot>
      <App />
    </RecoilRoot>
  </React.StrictMode>
);
  • npm i recoil을 통해 recoil을 다운로드
  • app.tsx나 index.tsx에 recoilRoot를 최상위 컴포넌트위로 씌워줌

Atoms

Atoms?

An atom represents state in Recoil. The atom() function returns a writeable RecoilState object.

  • atom은 Recoil에서 상태의 단위
  • atom이 업데이트 되면, 해당 atom을 구독하는 모든 컴포넌트들의 state가 새로운 값으로 리 렌더링됨

storage.ts

export const questionNum = atom({
    key: "questionNum",
    default : 0,
})
  • atom의 인자로 객체를 받음
  • 객체의 프로퍼티로는 key, default를 설정할 수 있음
  • key는 atoms의 키 값을 나타냄
  • default는 atom의 value를 나타내며 number, string, boolean 등의 기본형 객체, 배열 등의 참조형 모두 가능

useRecoilState

useCreate.ts

import { useRecoilState } from "recoil";
import { answerState, questionState } from "src/utils/storage";

export default function useCreate() {
    const [question, setQuestion] = useRecoilState(questionState);
    const [answer, setAnswer] = useRecoilState(answerState);

    const handleCreateQuestion = (question : string, answer : string) => {
        setQuestion('');
        setAnswer('');
    }

}
  • atoms의 value값을 가져오거나 value를 수정할 경우 사용하는 hook
  • useState와 마찬가지로 구조 분해 할당을 통해 배열로 atom state와 setAtoms 함수를 가져옴 (인자에 atoms를 추가하는 점이 다름)
  • set 함수를 통해 atom의 state를 변경 -> atom을 구독하는 모든 컴포넌트가 영향을 받으며 재 렌더링 됨

useRecoilValue

useLoadAllQuestion.ts

import { useRecoilValue } from "recoil";
import { questionNum } from "src/utils/storage";

export default function useLoadAllQuestion() {
    const questionNumber = useRecoilValue(questionNum);
    return {
        questionNumber,
    }
}
  • atoms의 current value를 가져올 경우 사용 하는 hook
  • atoms가 다른 컴포넌트에서 atoms의 value가 update 되는 경우 이 atoms을 구독하는 모든 컴포넌트에서 영향을 받아 변경 됨

useSetRecoilState

useDelete.ts

import { useSetRecoilState } from "recoil";
import { questionNum, questionSet } from "src/utils/storage";

export default function useDelete() {
    const setQuiz = useSetRecoilState(questionSet);

    const setAllQuestion = useSetRecoilState<number>(questionNum);

    const handleDeleteQuestion = (id:number) => {
        setQuiz((oldQuiz) => {
            const index = oldQuiz.findIndex(question => question.id === id);
            return [...oldQuiz.slice(0, index), ...oldQuiz.slice(index+1)];
        })
        setAllQuestion((prev:number) => prev-1);
    }

    return {
        handleDeleteQuestion
    }
}
  • atoms의 상태를 변경하는 경우 사용하는 hook

useResetRecoilState

  • useRecoilState나 useSetRecoilState로 update되던 atoms의 value를 reset

레퍼런스

pon06188님 - Recoil

https://velog.io/@pon06188/Recoil

juno7803님 - Recoil 200% 활용하기

https://velog.io/@juno7803/Recoil-Recoil-200-%ED%99%9C%EC%9A%A9%ED%95%98%EA%B8%B0#%EF%B8%8F-%EC%9D%B4-%EC%88%9C%EC%84%9C%EB%8C%80%EB%A1%9C-%EB%A7%90%EC%94%80-%EB%93%9C%EB%A6%B4%EA%B2%8C%EC%9A%94

wooder2050님 - 리코일(Recoil)은 왜 만든 건데?

https://velog.io/@wooder2050/%EB%A6%AC%EC%BD%94%EC%9D%BCRecoil%EB%8A%94-%EC%99%9C-%EB%A7%8C%EB%93%A0-%EA%B1%B4%EB%8D%B0

post-custom-banner

0개의 댓글