[React] 상태관리

Hannahhh·2022년 8월 31일
0

React

목록 보기
17/30

🔍 상태관리


상태는 동적으로 표현되는 데이터를 의미하며, 특정 컴포넌트 안에서만 관리되는 로컬 상태와 프로덕트 전체 혹은 여러 가지 컴포넌트가 동시에 관리되는 전역 상태로 구분할 수 있다.

  • 로컬 상태: 폼 데이터, input box, select box 등 입력값을 받는 경우(장바구니 예시: 선택한 수량)

  • 전역 상태: 데이터 로딩 여부, 라이트 모드/다크 모드 테마 설정, 국제화, Undo/Redo를 위한 History기능(포토샵, 일러스트에서 쓰임), (장바구니 예시: 장바구니에 담긴 물품, 상품 선택 여부)


서로 다른 컴포넌트가 사용하는 상태의 종류가 다르면, 서로 다른 출처가 있어도 상관없지만, 서로 다른 컴포넌트가 동일한 상태를 다룬다면, 출처는 오직 한 곳(전역 공간)이어야 한다.

따라서, 데이터의 무결성을 위해 동일한 데이터는 항상 같은 출처에서 가져오도록한다.(Single source of truth)




⚙ 상태관리 툴


  • ReactContext
  • Redux
  • Mobx

상태관리를 위해 상태관리 툴이 반드시 필요한 것은 아니지만, 위의 상태관리 툴을 이용하면, 전역 상태 저장소를 제공받을 수 있고, Props drilling 이슈를 해결할 수 있다.




👀 Props drilling


상위 컴포넌트의 state를 props를 통해 전달하고자 하는 컴포넌트로 전달하기 위해 그 사이에 있는 컴포넌트들은 props를 전달하는 용도로만 쓰이며 데이터를 전달하는 현상을 의미한다.

예를들어 순서대로 Component A~D가 존재한다고 했을 때, Component A의 state를 Component D로 전달하기 위해서는 Component B,C를 거쳐야하는 데, 이때 Component B,C는 props를 전달하는 용도로만 쓰이는 것이다.



✔ Props drilling의 문제점 및 해결방안


Props의 전달 횟수가 많지 않으면 큰 문제가 되지 않지만(5회 이하), 그 이상으로 늘어난다면 이하의 문제를 발생시킨다.

  • 코드의 가독성 떨어짐.

  • 코드의 유지보수가 힘들어짐.

  • 전달과정에서 props의 전달만을 위해 쓰이는 컴포넌트들 또한 리렌더링 되므로, 성능 약화를 야기함.


따라서, 과도한 props drilling을 방지하기 위해, 컴포넌트와 관련있는 state는 최대한 가까이 있을 수 있도록 유지하거나, 상태관리 라이브러리를 통해 전역으로 관리하는 저장소에서 직접 state를 꺼내쓸 수 있도록 한다.



⭐ props drilling 예시


  • 상태관리 라이브러리 사용x

// App.js

import React, { useState } from 'react';
import styled from 'styled-components';

// CSS 생략

export default function App() {
  const [number, setNumber] = useState(1);

  const plusNum = () => {
    setNumber(number + 1);
  };

  const minusNum = () => {
    setNumber(number - 1);
  };
  console.log('Parents');
  return (
    <Container>
      <Text>Parents Component</Text>
      <Text>
        가장 안 쪽 컴포넌트에 있는 버튼을 통해 state 변경
      </Text>
      <Quantity>{`state : ${number}`}</Quantity>
      <ComponentB plusNum={plusNum} minusNum={minusNum} />
    </Container>
  );
}

function ComponentB(
  {/* props로 전달받은 값 가져오기*/
    plusNum,minusNum
  }
) {
  return (
    <Container>
      <Text>Component B(props 전달 용도로만 쓰임)</Text>
      {/* props로 전달*/}
      <ComponentC plusNum={plusNum} minusNum={minusNum}/>
    </Container>
  );
}

function ComponentC({ plusNum, minusNum }) {
  return (
    <Container>
      <Text>ComponentC</Text>
      <Button onClick={plusNum}>👍</Button>
      <Button onClick={minusNum}>👎</Button>
    </Container>
  );
}



  • Redux 사용

// 전역으로 관리하는 저장소(js파일) 

export const reducer = (initialState = 1, action) => {
  let newState = initialState;
  switch (action.type) {
    case 'Plus':
      return newState + 1;
    case 'Minus':
      return newState - 1;
    default:
      return initialState;
  }
};

// App.js

import React, { useState } from 'react';
import styled from 'styled-components';
import { useSelector, useDispatch } from 'react-redux';

// css 생략

export default function App() {
  const number = useSelector((state) => state);
  return (
    <Container>
      <Text>Parents Component</Text>
      <Text>
        가장 안 쪽 컴포넌트에 있는 버튼을 통해 state 변경
      </Text>
      <Quantity>{`state : ${number}`}</Quantity>
      <ComponentB />
    </Container>
  );
}

function ComponentB() {
  return (
    <Container>
      <Text>Component B</Text>
      <ComponentC />
    </Container>
  );
}

function ComponentC() {
  const dispatch = useDispatch();
  const plusNum = () => {
    dispatch({ type: 'Plus' });
  };

  const minusNum = () => {
    dispatch({ type: 'Minus' });
  };
  return (
    <Container>
      <Text>Component C</Text>
      <Button onClick={plusNum}>👍</Button>
      <Button onClick={minusNum}>👎</Button>
    </Container>
  );
}




Reference: 코드스테이츠

0개의 댓글