Lifting State Up[React]

SnowCat·2023년 1월 9일
0

React - Main Concepts

목록 보기
9/11
post-thumbnail

※ 공식문서를 읽고 정리한 글입니다.

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

class Calculator extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.state = {temperature: ''};
  }

  handleChange(e) {
    this.setState({temperature: e.target.value});
  }

  render() {
    const temperature = this.state.temperature;
    return (
      <fieldset>
        <legend>Enter temperature in Celsius:</legend>
        <input
          value={temperature}
          onChange={this.handleChange} />
        <BoilingVerdict
          celsius={parseFloat(temperature)} />
      </fieldset>
    );
  }
}
  • 물의 온도를 입력으로 받아 끓는지 아닌지를 출력해주는 로직이다.
  • 화씨온도를 추가하려면 어떻게 해야할까?
const scaleNames = {
  c: 'Celsius',
  f: 'Fahrenheit'
};

class TemperatureInput extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.state = {temperature: ''};
  }

  handleChange(e) {
    this.setState({temperature: e.target.value});
  }

  render() {
    const temperature = this.state.temperature;
    const scale = this.props.scale;
    return (
      <fieldset>
        <legend>Enter temperature in {scaleNames[scale]}:</legend>
        <input value={temperature}
               onChange={this.handleChange} />
      </fieldset>
    );
  }
}

class Calculator extends React.Component {
  render() {
    return (
      <div>
        <TemperatureInput scale="c" />
        <TemperatureInput scale="f" />
      </div>
    );
  }
}
  • 앞서 있었던 Calculator 로직을 따로 TemperatureInput 컴포넌트로 분리하고, Calculator로직에는 scale prop을 받아 2개의 input 구역을 렌더링해줄 수 있다.
  • but 이 경우 섭시, 화씨온도의 state가 동기화되지 않는 문제가 발생하게 된다.

Lifting state

  • state를 공유하기 위해서 컴포넌트간의 가장 가까운 ancestor로 state를 옮겨야 하며, 이를 lifting state라고 함
  • 온도 state를 공유하기위한 컴포넌트 -> Calculator 컴포넌트
  • 우선 TemperatureInput 컴포넌트부터 바꿔보자
//...
  handleChange(e) {
    // Before: this.setState({temperature: e.target.value});
    this.props.onTemperatureChange(e.target.value);
    // ...

  render() {
    // Before: const temperature = this.state.temperature;
    const temperature = this.props.temperature;
    // ...
  • Lifting state로 인해 temperature는 Calculator 컴포넌트에서 관리된다. 따라서 render() 메서드에서 temperature값은 prop에서 가져와야함
  • temperature값은 prop에서 관리되기때문에 더이상 TemperatureInput에서는 제어 불가능 -> prop(Calculator) 메서드를 만들어 이벤트 발생시 호출해야함
class Calculator extends React.Component {
  constructor(props) {
    super(props);
    this.handleCelsiusChange = this.handleCelsiusChange.bind(this);
    this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this);
    this.state = {temperature: '', scale: 'c'};
  }

  handleCelsiusChange(temperature) {
    this.setState({scale: 'c', temperature});
  }

  handleFahrenheitChange(temperature) {
    this.setState({scale: 'f', temperature});
  }

  render() {
    const scale = this.state.scale;
    const temperature = this.state.temperature;
    const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature;
    const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature;

    return (
      <div>
        <TemperatureInput
          scale="c"
          temperature={celsius}
          onTemperatureChange={this.handleCelsiusChange} />
        <TemperatureInput
          scale="f"
          temperature={fahrenheit}
          onTemperatureChange={this.handleFahrenheitChange} />
        <BoilingVerdict
          celsius={parseFloat(celsius)} />
      </div>
    );
  }
}
  • Calculator 메서드는 state를 저장하고, scale과 temperature값을 기억해 TemperatureInput값이 바뀔때마다 값을 수정하고 BoilingVerdict 여부를 확인할 수 있게 됨
  • 공통 조상에서 데이터를 관리하는 하향식 데이터 흐름을 사용함으로써 버그의 생성 가능성을 크게 줄일 수 있으며, 사용자의 입력을 제어할 수 도 있음
  • 예시처럼 데이터가 prop이나 state에서 계산될수 있으면, 그 값은 Lifting state 해야함

출처:
https://ko.reactjs.org/docs/lifting-state-up.html

profile
냐아아아아아아아아앙

0개의 댓글