[TIL] 220326

Lee Syong·2022년 3월 26일
0

TIL

목록 보기
204/204
post-thumbnail

📝 오늘 한 것

  1. react data-flow : state 끌어올리기(하향식 데이터 흐름)

📚 배운 것

1. data-flow

1) State 끌어올리기

(1) 과제: 주어진 온도에서 물의 끓는 여부를 추정하는 온도 계산기 만들기

  • 먼저, 물의 온도에 따라 끓는 여부를 알려주는 BoilingVerdict 컴포넌트를 만든다.
    다음으로, 물의 온도를 입력할 수 있는 input이 필요한데, Calculator 컴포넌트를 만들어서 그 안에 섭씨 온도를 입력할 수 있는 input을 만들도록 한다.

    Calculator 컴포넌트는 섭씨 온도 input과 함께 BoilingVerdict 컴포넌트를 return 해야 한다.
    섭씨 input에 물의 온도를 입력하면 화면에 '물이 끓는다/안 끓는다' 텍스트가 보인다.

  • 섭씨 온도뿐 아니라 화씨 온도를 입력할 수 있는 input도 만들어야 한다.
    TemperatureInput 컴포넌트를 만들어 재사용할 수 있다.

    이때 temperature state는 'TemperatureInput 컴포넌트 안에서' return되는 input의 value 값으로 쓰일 수 있다.
    그러나, 이렇게 하면 temperature state가 Calculator 컴포넌트의 자식 컴포넌트인 TemperatureInput 컴포넌트 안에 존재하게 되어 Calculator가 return 하는 BoilingVerdict 컴포넌트에 temperature state의 값을 전해줄 수가 없다.

  • 위 문제를 해결하려면 temperature state를 공유할 수 있어야 한다.
    React에서 state를 공유하는 것은 해당 state을 필요로 하는 컴포넌트 간의 가장 가까운 공통 조상으로 state를 끌어올림으로써 이뤄낼 수 있다.

    여기서 temperature state를 필요로 하는 것은 TemperatureInput 컴포넌트와 Calculator 컴포넌트이므로 TemperatureInput 컴포넌트에 있던 temperature state를 Calculator 컴포넌트로 옮기도록 한다.
    Calculator 컴포넌트에 temperature state를 선언하고 Calculator 컴포넌트의 자식 컴포넌트인 TemperatureInput 컴포넌트에는 props.temperature의 형태로 그 값을 전달한다.
    한편, 제어 컴포넌트인 TemperatureInput에서 사용자가 입력한 값에 따라 value를 변화시키기 위해서는 이벤트 리스너도 필요하기 때문에 iuput의 value가 변화하는 것을 감지하는 이벤트 리스너 함수 handleTemperatureChange 또한 props.handleTemperatureChange의 형태로 TemperatureInput 컴포넌트에 전달하도록 한다.

  • (현재 임시로 만들어준 바에 따르면) 섭씨 온도 temperatureInput 컴포넌트와 화씨 온도 temperatureInput 컴포넌트에 같은 값의 props.temperature와 props.handleTemperature을 전해준 상태이기 때문에 온도를 어떤 input 창에 입력하든 두 input 창에서 동일한 값을 보게 된다.

  • 이제 섭씨 온도 input의 입력값과 화씨 온도 input의 입력값이 서로 동기화되도록 해야 한다.
    즉, 섭씨 온도 input에 온도를 입력하면 해당 온도가 자동으로 화씨 온도로 변환되어 화씨 온도 input에 보이도록 해야 하고, 반대의 경우에도 마찬가지다.

    [TIL] 220121 中 (2) hours input에 onChange 이벤트 등록하기 참고
    위에서는 flipped 값에 따라 동시에 2개의 input에 보여질 값을 각각 설정했었다.

    📌 여기서도 마찬가지로 어떤 input에 값을 입력했는지에 따라 각각의 input에 보여지는 값이 달라지도록 설정하기 위해 scale state를 이용할 수 있다.
    각각의 input의 value가 변화하면 실행되는 각각의 이벤트 리스너 함수(handleCelsiusChange, handleFahrenheitChange)에서 scale state의 값을 각각 c 또는 f로 바꿔주는 setScale 함수가 실행되도록 한 것이다.

  • 그 외에도 input 값을 초기화하는 부분 등을 추가하여 최종적으로 코드를 완성했다.

const scales = {
  c: "섭씨(celsius)",
  f: "화씨(Fahrenheit)",
};

function Calculator() {
  const [temperature, setTemperature] = React.useState("");
  const [scale, setScale] = React.useState("c");

  function handleCelsiusChange(event) {
    if (event.target.value === "") {
      setTemperature("");
      
      return;
    }

    setTemperature(event.target.value);
    setScale("c");
  }
  
  function handleFahrenheitChange(event) {
    if (event.target.value === "") {
      setTemperature("");
      
      return;
    }
    
    setTemperature(event.target.value);
    setScale("f");
  }
  
  function toFahrenheit(celsius) {
    return celsius === "" ? "" : (celsius * 1.8) + 32;
  }
  
  function toCelsius(fahrenheit) {
    return fahrenheit === "" ? "" : (fahrenheit - 32) / 1.8;
  }
  
  return (
    <div>
      <TemperatureInput
        scale="c"
        temperature={scale === "c" ? temperature : toCelsius(temperature)}
        handleTemperatureChange={handleCelsiusChange}
      />
      <TemperatureInput
        scale="f"
        temperature={scale === "f" ? temperature : toFahrenheit(temperature)} {/* 📌 2. 바뀐 scale state(f)를 기반으로 화씨 input의 value 값 결정 */}
        handleTemperatureChange={handleFahrenheitChange} {/* 📌 1. 화씨 input에 입력하는 순간 scale state를 화씨(f)로 바꿈 */}
      />
      <BoilingVerdict celsius={scale === "c" ? temperature : toCelsius(temperature)} />
    </div>
  );
}

function TemperatureInput({scale, temperature, handleTemperatureChange}) {
  return (
    <fieldset>
      <label htmlFor={scales[scale]}>{scales[scale]}: </label>
      <input
        text="number"
        id={scales[scale]} {/* 대괄호 표기법 사용 (점 표기법 X) */}
        value={temperature}
        onChange={handleTemperatureChange}
      />
    </fieldset>
  );
}

function BoilingVerdict({celsius}) {
  if (celsius >= 100) {
    return <p>물이 끓을 것입니다.</p>;
  }
  
  return <p>물이 끓지 않을 것입니다.</p>;
}

ReactDOM.render(<Calculator />, document.getElementById('root'));

(2) 교훈 및 정리

기본적으로 state는 렌더링 시 그 값을 필요로 하는 컴포넌트에 일단 먼저 추가해야 한다.
그 후 다른 컴포넌트 또한 해당 state 값을 필요로 한다면, 그들의 가장 가까운 공통 조상으로 해당 state를 끌어올린다.(하향식 데이터 흐름 구현)

state 끌어올리기를 활용하면 다른 컴포넌트 간에 존재하는 state를 동기화시키는 것보다 버그를 찾고 격리하기가 더 쉽다.
또한 위에서 살펴본 것처럼 사용자의 입력을 변형하거나 거부하는 자체 로직도 구현할 수 있다.

주의할 점은 어떤 값이 props 또는 state로부터 계산되는 것이라면, 그 값을 state에 두어서는 안 된다는 것이다.

위 예제에서 celsiusValue와 fahrenheitValue는 temperature state로부터 계산되는 값이기 때문에 이들을 직접 state에 저장하는 대신, 가장 최근에 변경된 temperature과 scale만 state로 저장한 것을 예로 들 수 있다.
input의 value는 항상 temperature 값과 scale 값에 기반하여 render() 메서드 안에서 계산된다.
이를 통해 하나의 input에 사용자가 입력한 값, 즉 value의 정밀도를 유지한 채 이를 이용해 다른 input의 value를 조정할 수 있다.


✨ 내일 할 것

  1. react opimization
profile
능동적으로 살자, 행복하게😁

0개의 댓글