React- State 끌어올리기

이수현·2022년 5월 25일
0

React

목록 보기
3/4

📚State 끌어올리기

종종 동일한 데이터에 대한 변경사항을 여러 컴포넌트에 반영해야 할 필요가 있다.

  • 이럴 때 가장 가까운 공통의 피상속 컴포넌트로 state를 끌어올리는 것이 좋다.
  • 어떻게 할 수 있는지 살펴보자.
  • 공식문서 내용을 하나하나 따라하며 원리를 이해해보자.

Calculator 컴포넌트

import { useState } from "react";
import BoilingVerdict from "./BoilingVerdict";
import TemperatureInput from "./TemperatureInput";

function Calculator() {
  const [temperatureInfo, setTemperatureInfo] = useState({
    temperature: "",
    scale: "",
  });

  const toCelsius = (fahrenheit) => ((fahrenheit - 32) * 5) / 9;
  const toFahrenheit = (celsius) => (celsius * 9) / 5 + 32;

  const tryConvert = (temperature, convert) => {
    const input = parseFloat(temperature);

    if (Number.isNaN(input)) return "";

    const output = convert(input);

    const rounded = Math.round(output * 1000) / 1000;

    return rounded.toString();
  };

  const handleCelsiusChange = (temperature) => {
    setTemperatureInfo({ ...temperatureInfo, scale: "c", temperature });
  };

  const handleFahrenheitChange = (temperature) => {
    setTemperatureInfo({ ...temperatureInfo, scale: "f", temperature });
  };
  const celsius =
    temperatureInfo.scale === "f"
      ? tryConvert(temperatureInfo.temperature, toCelsius)
      : temperatureInfo.temperature;
  const fahrenheit =
    temperatureInfo.scale === "c"
      ? tryConvert(temperatureInfo.temperature, toFahrenheit)
      : temperatureInfo.temperature;
  return (
    <div>
      <TemperatureInput
        scale="c"
        temperature={celsius}
        onTemperatureChange={handleCelsiusChange}
      />
      <TemperatureInput
        scale="f"
        temperature={fahrenheit}
        onTemperatureChange={handleFahrenheitChange}
      />
      <BoilingVerdict celsius={parseFloat(celsius)} />
    </div>
  );
}

export default Calculator;

Calculator 컴포넌트로 temperature라는 상태가 끌어올려졌다.

  • 각각의 Temperature 태그에 값이 입력될 때마다 Calculator 컴포넌트는 입력받은 값을 삼항연산자로 판별하여 변환하고, 변환된 값은 입력받지 않은 태그의 value로 입력해준다.
  • 여기서 중요한 점은 2개의 컴포넌트에서 사용되는 temperature 값을 가장 가까운 공통의 피상속 컴포넌트인 Calculator로 끌어올려서 사용하고 있다는 점이다.

TeperatureInput 컴포넌트

function TemperatureInput({ temperature, scale, onTemperatureChange }) {
  const handleChange = (e) => {
    onTemperatureChange(e.target.value);
  };

  return (
    <fieldset>
      <legend>
        온도를 입력하세요.({scale === "c" ? "Celsius" : "Fahrenheit"})
      </legend>
      <input value={temperature} onChange={handleChange} />
    </fieldset>
  );
}

export default TemperatureInput;

TemperatureInput 컴포넌트에서는 Calculator에게 props객체로 받은 temperature, scale, onTemperatureChange 프로퍼티들을 구조 분해하여 사용하고 있다.

  • 여기서 onTemperatureChange는 TeperatureInput 컴포넌트의 입력값이 변할 때 호출된다.
  • onTemperatureChange 함수가 호출될 때 현재 입력받은 e.target.value를 매개변수로 넣어 전달된다.
  • onTemperatureChange라는 이름으로 전달받는 함수는 섭씨와 화씨일 때 다른 함수가 담겨 전달된다.
  • 섭씨일 때는 Calculator 컴포넌트에 정의된 handleCelsiusChange함수가 담겨있고, 화씨일 때는 handleFahrenheitChange함수가 담겨있다.
  • 각 함수가 호출될 때 setTemperatureInfo()를 통해 React에게 자신을 다시 렌더링하도록 요청하는 것을 알 수 있다.
  • 즉, input태그에 값이 입력될 때마다 Calculator 컴포넌트에 정의된 로직을 거쳐(렌더링도 됨), 섭씨input에 입력했을 때는 화씨input에 섭씨input에 적힌 숫자를 화씨로 변환된 값이 입력되며(렌더링될 때) 반대도 마찬가지이다.

BoilingVerdict 컴포넌트

function BoilingVerdict({ celsius }) {
  return celsius >= 100 ? (
    <p>The water would boil.</p>
  ) : (
    <p>The water would not boil.</p>
  );
}

export default BoilingVerdict;

BoilingVerdict 컴포넌트에서는 Calculator에서 props객체로 내려준 celsius 프로퍼티를 구조 분해해서 사용하고 있다.

  • Calculator의 상태값을 내려받아 사용하고 있다!

중요사항

React 애플리케이션 안에서 변경이 일어나는 데이터에 대해서는 "source of truth"를 하나만 두어야 한다.

  • 풀어서 얘기하자면 보통의 경우는 state는 렌더링에 그 값을 필요로 하는 컴포넌트에 먼저 추가되는데, 다른 컴포넌트도 역시 그 값이 필요하게 되면 그 값을 가장 가까운 공통의 피상속 컴포넌트로 끌어올리면 된다는 것이다.
  • 다른 컴포넌트 간에 존재하는 state를 동기화 시키려고 노력하는 대신 하향식 데이터 흐름을 추천한다고 한다.
  • 물론, state를 끌어올리는 작업은 각 컴포넌트에서 접근하는 양방향 바인딩 접근 방식보다 더 많은 "보일러 플레이트" 코드를 유발하지만, 버그를 찾고 격리하기 더 쉽게 만든다는 장점이 있다.
  • 어떤 값이 props 또는 state로부터 계산될 수 있다면, 아마 그 값을 state에 둬서는 안된다.

0개의 댓글