React学習3(props, single source of truth, controlled component)
React復習がてら要点をまとめていく。
Single source of truth
Reactでの状態(state)管理は、一つのソースで行わないといけない。これがSingle source of truthと言われるものである。 あるlocal stateを持ったコンポーネントを複数読み込んでその親もstateを持つとすると、stateは親のコンポーネント管理下でなければいけない。
具体的な例として、counterコンポーネントが複数読み込まれているというケースを考える。
Counters.jsx
(親コンポーネント)
import React, { Component } from 'react' import Counter from './Counter' class Counters extends Component { state = { counters: [ { id: 1, value: 0 }, { id: 2, value: 0 }, { id: 3, value: 3 }, { id: 4, value: 0 }, ], } render() { const { counters } = this.state if (counters.length === 0) return <p>Nothing!</p> return ( <> <button onClick={this.handleReset} className="btn btn-primary btn-sm m-2" > Reset </button> {counters.map(counter => ( <Counter key={counter.id} value={counter.value} selected={true} onDelete={() => this.handleDelete(counter)} /> ))} </> ) } handleReset = () => { const counters = this.state.counters.map(c => { c.value = 0 return c }) this.setState({ counters, }) } handleDelete = counter => { const { counters } = this.state const filteredCounters = counters.filter(c => { return c !== counter }) this.setState({ counters: filteredCounters, }) } } export default Counters
Counter.jsx
(子コンポーネント)
import React, { Component } from 'react' class Counter extends Component { state = { count: this.props.value, selected: this.props.selected, } render() { return <>{this.renderCounter()}</> } handleIncrement = product => { this.setState({ count: this.state.count + 1, }) } renderCounter() { return ( <div> <span className={this.getBadgeClasses()}>{this.formatCount()}</span> <button onClick={this.handleIncrement} className="btn btn-secondary btn-sm" > Increment </button> <button onClick={this.props.onDelete} className="btn btn-danger btn-sm m-2" > Delete </button> </div> ) } getBadgeClasses() { let classes = 'badge m-2 badge-' classes += this.state.count === 0 ? 'warning' : 'primary' return classes } formatCount() { const { count } = this.state return count === 0 ? 'Zero' : count } } export default Counter
この例だとResetボタンが効かない。子コンポーネントと親コンポーネントの両方で重複したstateを持っているからだ。
そこでリファクタリングを行い、親コンポーネントに全てのstateを集約させる。
ちなみにこういう場合、子コンポーネントのことをControlled component
と呼ぶ。