React 상태 관리와 불변성

HanSungUk·2022년 12월 21일
0

React

목록 보기
13/13
post-thumbnail
post-custom-banner

React 상태 관리와 불변성(1)

상태 관리에 관한 리액트 강의에서 불변성에 관한 내용이 언급되어 정리하고자 합니다.

1. 불변성이란?

변수 값을 변경하기 위해 원시 값을 재할당하면 새로운 메모리 공간을 확보하고 재할당한 값을 저장한 후, 변수가 참조하던 메모리 공간의 주소를 변경한다. 값의 이러한 특성을 불변성이라 합니다. - 모던 자바스크립트

일반적으로 데이터 변경에는 두 가지 방법이 있습니다.
첫 번째는 데이터의 값을 직접 변경하는 것입니다.
두 번째는 원하는 변경 값을 가진 새로운 사본으로 데이터를 교체하는 것입니다.

최종 결과는 동일하지만 직접적인 객체 변경이나 기본 데이터의 변경을 하지 않는다면 아래 불변성의 이점을 가질 수 있습니다.

2. 리액트에서 상태가 불변해야하는 이유

리액트 공식 문서에서는 불변성의 이점에 대해 다음과 같이 기술했습니다.

  1. 복잡한 특징들을 단순하게 만듬
    불변성은 복잡한 특징들을 구현하기 쉽게 만듭니다.
    여기서 복잡한 특징이란 특정 행동을 취소하고 다시 실행하는 기능을 의미합니다.
    직접적인 데이터 변이를 피하는 것은 이전 데이터를 유지하고 나중에 재사용할 수 있게 만듭니다.

  2. 변화를 감지함
    리액트는 참조하고 있는 불변 객체가 이전 객체와 다르다면 객체가 변한 것이라고 판단하고 상태를 업데이트 합니다.
    하지만 객체가 직접적으로 수정돠면 변화를 감지하는 것이 어렵습니다.

  3. React에서 다시 렌더링하는 시기를 결정함
    불변성의 가장 큰 장점은 React에서 순수 컴포넌트를 만드는 데 도움을 준다는 것입니다. 변하지 않는 데이터는 변경이 이루어졌는지 쉽게 판단할 수 있으며 이를 바탕으로 컴포넌트가 다시 렌더링할지를 결정할 수 있습니다.

import React, { useState } from 'react';

export default function AppMentor() {
  const [person, setPerson] = useState({
    name: '테리',
    title: '개발자',
    mentors: [
      {
        name: '밥',
        title: '주니어개발자',
      },
      {
        name: '톰',
        title: '시니어개발자',
      },
    ],
  });
  return (
    <div>
      <h1>
        {person.name}{person.title}
      </h1>
      <p>{person.name}의 멘토는:</p>
      <ul>
        {person.mentors.map((mentor, index) => (
          <li key={index}>
            {mentor.name} ({mentor.title})
          </li>
        ))}
      </ul>
      <button
        onClick={() => {
          const prev = prompt(`누구의 이름을 바꾸고 싶은가요?`);
          const current = prompt(`이름을 무엇으로 바꾸고 싶은가요?`);
          let newMentor = person.mentors.filter(
            (person) => person.name === prev
          );
          newMentor.name = current;
          setPerson((person) => ({
            ...person,
            mentors: person.mentors.map((mentor) => {
              if (mentor.name === prev) {
                return { ...mentor, name: current };
              }
              return mentor;
            }),
          }));
        }}
      >
        멘토의 이름을 바꾸기
      </button>
    </div>
  );
}

3. Reducer 살펴보기

import React, { useReducer } from 'react';
import personReducer from './reducer/person-reducer';

export default function AppMentor() {
  // const [person, setPerson] = useState(initialPerson);

  const [person, dispatch] = useReducer(personReducer, initialPerson);

  const handleChange = () => {
    const prev = prompt(`누구의 이름을 바꾸고 싶은가요?`);
    const current = prompt(`이름을 무엇으로 바꾸고 싶은가요?`);
    dispatch({ type: 'updated', prev, current });
  };

  const handleAdd = () => {
    const name = prompt(`누구의 이름을 추가하고 싶으신가요?`);
    const title = prompt(`무슨 레벨의 개발자인가요?`);

    dispatch({ type: 'added', name, title });
  };

  const handleDelete = () => {
    const name = prompt(`누구의 이름을 삭제하고 싶으신가요?`);

    dispatch({ type: 'deleted', name });
  };
  return (
    <div>
      <h1>
        {person.name}{person.title}
      </h1>
      <p>{person.name}의 멘토는:</p>
      <ul>
        {person.mentors.map((mentor, index) => (
          <li key={index}>
            {mentor.name} ({mentor.title})
          </li>
        ))}
      </ul>
      <button onClick={handleChange}>멘토의 이름을 바꾸기</button>
      <button onClick={handleAdd}>멘토 추가하기</button>
      <button onClick={handleDelete}>멘토 삭제하기</button>
    </div>
  );
}

const initialPerson = {
    name: '테리',
    title: '개발자',
    mentors: [
      {
        name: '밥',
        title: '주니어개발자',
      },
      {
        name: '톰',
        title: '시니어개발자',
      },
    ],
  };
const personReducer = (person, action) => {
  switch (action.type) {
    case 'updated': {
      const { prev, current } = action;
      return {
        ...person,
        mentors: person.mentors.map((mentor) => {
          if (mentor.name === prev) {
            return { ...mentor, name: current };
          }
          return mentor;
        }),
      };
    }
    case 'added': {
      const { name, title } = action;
      return {
        ...person,
        mentors: [{ name, title }, ...person.mentors],
      };
    }
    case 'deleted': {
      const { name } = action;
      return {
        ...person,
        mentors: person.mentors.filter((mentor) => mentor.name !== name),
      };
    }
    default: {
      throw Error(`알 수 없는 액션 타입이다: ${action.type}`);
    }
  }
};

export default personReducer;

참고 문헌
https://ko.reactjs.org/tutorial/tutorial.html#why-immutability-is-important

post-custom-banner

0개의 댓글