React練習 – 割り勘計算アプリ(2)

サンプル開発

前回の投稿では、ヘッダーコンポーネントと明細コンポーネントを作成し、固定値をプロパティで渡すところまで実装した。次のステップとして、これらのコンポーネントをまとめる親コンポーネントを作成し、親コンポーネントのstateに保持するデータを渡すように修正する。

コンポーネントのstateの初期状態をconstructorでセットする。

// 割り勘計算機コンポーネント
class BillSplitter extends Component {

  constructor(props) {
    super(props);
    this.state = {
      payment: 0, //支払額
      total: 0, //合計額
      remainder: 0, //端数
      participants: new Array(3).fill().map(() => ({name: '', kind: 'fix', amount: 0})), //参加者
    };
  }

render関数で、HeaderコンポーネントとDetailコンポーネント(複数)を配置して返却する。

  render () {
    const details = this.state.participants.map(person => {
      return <Detail {...person} />
    })
    return (
      <>
        <Header
          payment={this.state.payment}
          total={this.state.total}
          remainder={this.state.remainder}
          participantsNumber={this.state.participants.length}
        />
        {details}
      </>
    );
  }
  • コンポーネントのルート要素は1つでなければならないので、<></>(React.Fragmentの簡略記法)をプレースホルダとして使用する(6行目、14行目)
  • 明細の繰り返し出力は、配列をmapしてJSXで記述したDetailエレメントの配列に変換している(2-4行目)
  • 子コンポーネントのプロパティはスプレッド演算子でまとめて展開可能(3行目)

まだ現時点ではデータ表示するだけで、変更することができない。次回は、ヘッダー部の支払額を変更すると、親のstateが更新されて割り勘計算が走り、ヘッダーと明細の各コンポーネントが更新される処理を実装しようと思う。

画面イメージ

現時点でのソースコード(App.js)は以下のとおり。

import React, {Component} from 'react';
import './App.css';

// ヘッダーコンポーネント
class Header extends Component {
  render () {
    return (
      <div>
        <div>
          <label>支払額:<input type="text" value={this.props.payment} /></label>
        </div>
        <div>
          <label>合計額:<span>{this.props.total}</span></label>
        </div>
        <div>
          <label>端数:<span>{this.props.remainder}</span></label>
        </div>
        <div>
          <label>人数:<input type="text" value={this.props.participantsNumber} /></label>
        </div>
      </div>
    );
  }
}

// 明細コンポーネント
class Detail extends Component {
  render () {
    return (
      <div>
        <input type="text" value={this.props.name} />
        <label><input type="radio" value="fix" checked={this.props.kind === 'fix'} />固定</label>
        <label><input type="radio" value="split" checked={this.props.kind === 'split'} />割り勘</label>
        <input type="text" value={this.props.amount} />
      </div>
    );
  }
}

// 割り勘計算機コンポーネント
class BillSplitter extends Component {

  constructor(props) {
    super(props);
    this.state = {
      payment: 0, //支払額
      total: 0, //合計額
      remainder: 0, //端数
      participants: new Array(3).fill().map(() => ({name: '', kind: 'fix', amount: 0})), //参加者
    };
  }

  render () {
    const details = this.state.participants.map(person => {
      return <Detail {...person} />
    })
    return (
      <>
        <Header
          payment={this.state.payment}
          total={this.state.total}
          remainder={this.state.remainder}
          participantsNumber={this.state.participants.length}
        />
        {details}
      </>
    );
  }
}

function App () {
  return (
    <div className="App">
      <BillSplitter />
    </div>
  );
}

export default App;

コメント

タイトルとURLをコピーしました