Reactガイドを読んでいくその16

これは

Reactのガイドを読んでいく記事です。

ガイドのリンク

ja.reactjs.org

state のリフトアップ

stateのリフトアップ

いよいよ本章のテーマにきました。

昨日までの状態ではTemperatureInputは独立してローカルのstateを保持しています。
理想は入力フィールドは同期されていて、片方を入力するともう片方が埋まる状態にしておきたいです。

Reactでstateを共有したい場合は両者の共通の親コンポーネントに移動することで実現が可能。
これをリフトアップと呼ぶ。

TemperatureInputのstateをCalculatorに移動させる。
Calculatorが両者の共通の存在になることで、摂氏変換、華氏変換いずれもアクセスできるようになり、同期されるようになる。

リフトアップを行なっていきます。
TemperatureInputのthis.state.temperatureをthis.props.temperatureに変更。

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() {
    <b>// this.state.temperature</b>
    const temperature = <b>this.props.temperature</b>;
    const scale = this.props.scale;
    return (
      <fieldset>
        <legend>Enter temperature in {scaleNames[scale]}:</legend>
        <input value={temperature}
               onChange={this.handleChange} />
      </fieldset>
    );
  }
}

このままだとpropsは読み取り専用であり、propsであるがゆえにTemperatureInputは制御できない状態になっている。
なので更新のためにthis.props.onTemperatureChangeを呼び出すように修正。

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

  handleChange(e) {
  <b>// this.setState({temperature: e.target.value});を変更</b>
    this.props.onTemperatureChange(e.target.value);
  }

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

onTemperatureChangeはCalculatorからtemperatureプロパティと共に渡されるように作ります。
現在のCalculatorは以下のようになっています。

class Calculator extends React.Component {
  render() {
    return (
      <div>
        <TemperatureInput scale="c" />
        <TemperatureInput scale="f" />
      </div>
    );
  }
}

temperatureとscaleをローカルなstateに保存するようにします。

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>
    );
  }
}

これで両者の同期が取れ、片方を入力すると変換された値がもう一つの方にセットされるようになりました。

今日はここまで。