React 전역상태 관리

경규혁·2023년 5월 26일
0
post-thumbnail

상태란?

"어플리케이션의 화면에 영향을 끼치는 자바스크립트 객체" 라고 생각하면 된다. 보통은 이를 통칭해 "변화하는 데이터" 라고 일컫는다.

상태의 종류?

  • 지역상태 (Local State)
  • 컴포넌트간의 상태 (Cross Component State)
  • 전역상태 (Global State)

지역상태란?

특정 컴포넌트 안에서만 관리되는 상태를 뜻한다. 다른 컴포넌트들과 데이터를 공유하지 않는다. 예를 들면, input, selectbox 등에서 사용자의 입력값을 받는 경우가 있다. (보통 Form 데이터들이 지역상태에 속한다.)

전역상태란?

프로젝트 전체에 영향을 끼치는 상태이다. 예를 들면, 유저 기능을 생각하면 된다. 이 또한 Prop Drilling 방식을 활용해서 부모에서 자식으로 데이터를 전달한다.

컴포넌트간 상태란?

여러가지 컴포넌트에서 관리되는 상태를 나타낸다. 다수의 컴포넌트에서 쓰이고, 또 영향을 미치는 상태를 뜻한다. 프로젝트 곳곳에서 쓰이는 모달을 예로 들 수 있다. 보통 상위 컴포넌트에서 하위 컴포넌트로 prop을 넘겨 해당 컴포넌트까지 전달되도록 하는 Prop Drilling 방식을 필요로 한다.

Prop Drilling 이란?

props를 오직 하위 컴포넌트로 전달하는 용도로만 쓰이는 컴포넌트를 거치며 컴포넌트에서 다른 컴포넌트로 데이터를 전달하는 과정이다.
[상위 컴포넌트 > 중간 컴포넌트 > 중간 컴포넌트 > ... > 타겟 컴포넌트] 이런 식이다.
이런 prop 전달 과정이 많지 않다면 큰 걱정은 없지만, 만약 어플리케이션이 커져서 정말 많은 과정을 거치게 되어야 한다면, 그 코드의 prop을 추적하기는 정말 힘들 것이다.

Context API란?

React 컴포넌트 트리 안에서 전역 상태를 공유할 수 있도록 만들어진 방법이다.

  • Context API는 중간에 있는 Element들에게 props를 넘겨주지 않고도 데이터를 가져다가 사용할 수 있기 때문에 (Prop Drilling을 피할 수 있다.) 테마나 언어 등 전역적으로 쓰이는 데이터들을 사용할 때 자주 쓰인다.
  • 다만 Context를 사용하게되면 컴포넌트를 재사용하기가 매우 힘들어지기때문에, 마구 사용하는 건 지양해야한다.

Context API는 Context, Provider, Consumer로 설명할 수 있다.

Context는 전역 상태를 저장하는 곳이다.
Context 내부에 Provider와 Consumer가 정의되어있고, Consumer는 Context를 통해서 상태에 접근이 가능하다.

Provider는 전역 상태를 제공하는 역할을 한다.
Context에 상태를 제공해서 다른 컴포넌트가 상태에 접근할 수 있도록 도와준다. 제공된 상태에 접근하기 위해서는 Provider 하위에 컴포넌트가 포함되어있어야한다. 보통 모든 컴포넌트에 접근해야하는 상태를 제공하기 위해서는 Root Component (index.js / app.js)에서 Provider를 정의한다.

Consumer는 제공받은 전역 상태를 받아서 사용하는 역할을 한다.


Context API 사용법

import { createContext } from 'react';
const MyContext = createContext();
function App() {
  return (
    <MyContext.Provider value="Hello World">
      <GrandParent />
    </MyContext.Provider>
  );
}

Context 객체 안에는 Provider라는 컴포넌트가 들어있다. 그리고, 그 컴포넌트간에 공유하고자 하는 값을 value 라는 Props로 설정하면 자식 컴포넌트들에서 해당 값에 바로 접근을 할 수 있다.


  • 예시코드
import { createContext, useContext } from 'react';
const MyContext = createContext();

function App() {
  return (
    <MyContext.Provider value="Hello World">
      <GrandParent />
    </MyContext.Provider>
  );
}

function GrandParent() {
  return <Parent />;
}

function Parent() {
  return <Child />;
}

function Child() {
  return <GrandChild />;
}

function GrandChild() {
  return <Message />;
}

function Message() {
  const myMessage = useContext(MyContext);
  return <div>Received: {myMessage}</div>;
} // Hello World 출력

export default App;
  • Provider를 사용하지 않을 경우
    만약 자식 컴포넌트에서 useContext를 사용하고 있는데, Provider 컴포넌트로 감싸는 것을 깜빡하면 어떻게 될까?
    value 값을 따로 지정하지 않았기 때문에, undefined로 조회되어 해당 값이 보여질 자리에 아무것도 나타나지 않게된다.
    그러한 경우에 기본 값을 설정하고 싶다면, createContext 함수에 인자로 기본 값을 넣어주면 된다.
const MyContext = createContext('default value');

Recoil이란?

Recoil은 React 프로젝트를 위한 많은 전역 상태관리 라이브러리들 중 하나로, 2020년 5월 Facebook에서 출시하였다.
Recoil은 React 전용 라이브러리인 만큼 React 내부 접근성이 용이하다.

$ yarn add recoil

주요개념

Recoil을 사용하면 atoms (공유 상태)에서 selector(순수 함수)를 거쳐 React 컴포넌트로 내려가는 data-flow graph를 만들 수 있다.

먼저, Recoil을 활용하기 위해 루트 컴포넌트를 <RecoilRoot> 로 감싸주기만 하면 된다!

  1. Atoms는 컴포넌트가 구독할 수 있는 상태의 단위다.
  2. Selectors는 atoms 상태값을 동기 또는 비동기 방식을 통해 변환한다.

Atoms을 설정할 땐, Recoil의 atom() 메서드를 통해 변수에 할당해주면 된다. 이 때, key, default 2개의 프로퍼티를 필수로 설정해야한다.

  • key : 고유한 key 값 (보통 해당 atom을 생성하는 변수 명으로 지정한다.)
  • default : atom 의 초기값을 정의한다. 정적인 값(int, string...), promise, 다른 atom 의 값으로 설정할 수 있다.
const textState = atom({
  key: 'textState', // unique ID (with respect to other atoms/selectors)
  default: '', // default value (aka initial value)
});

컴포넌트가 atom을 읽고 쓰게 하기 위해서는 useRecoilState()를 아래와 같이 사용하면 된다.

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>
  );
}

Selector를 설정할 때도, Recoil의 selector() 메서드를 통해 등록하면 된다. 기본적으론, key와 get 2개의 프로퍼티를 설정한다.

  • key : 고유한 key 값
  • get : Selector 순수함수. 사용할 값을 반환하며, 매개변수인 콜백객체 내 get() 메서드로 다른 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}</>;
}
profile
갓생살기 기원!

0개의 댓글