React学習4(functional component)
React復習がてら要点をまとめていく。
React学習3で学んだところから、さらに階層深くコンポーネント構成をする、次のようなケースの場合。
. ├── App.jsx ├── components │ ├── Counter.jsx │ ├── Counters.jsx │ └── Navbar.jsx └── index.js
Objectやargument destructuringもふんだんに利用しながら記述していく。
App.jsx
import React, { Component } from 'react' import _ from 'lodash' import Navbar from './components/Navbar' import Counters from './components/Counters' class App extends Component { state = { counters: [ { id: 1, value: 2 }, { id: 2, value: 0 }, { id: 3, value: 3 }, { id: 4, value: 4 }, ], } render() { const { counters } = this.state return ( <div className="App"> <Navbar totalCounters={counters.length > 0 && counters.length} /> <div className="container"> <Counters counters={counters} onReset={this.handleReset} onIncrement={this.handleIncrement} onDelete={this.handleDelete} /> </div> </div> ) } handleIncrement = counter => { const { counters } = this.state const clickedCounterIndex = counters.findIndex(c => c.id === counter.id) const countersIncremented = _.cloneDeep(counters) countersIncremented[clickedCounterIndex].value++ this.setState({ counters: countersIncremented, }) } 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 App
Counters.jsx
import React, { Component } from 'react' import Counter from './Counter' class Counters extends Component { render() { const { counters, onReset, onIncrement, onDelete } = this.props if (counters.length === 0) return <p>Nothing!</p> return ( <> <button onClick={onReset} className="btn btn-primary btn-sm m-2"> Reset </button> {counters.map(counter => ( <Counter key={counter.id} value={counter.value} selected={true} onIncrement={() => onIncrement(counter)} onDelete={() => onDelete(counter)} /> ))} </> ) } } export default Counters
Counter.jsx
import React, { Component } from 'react' class Counter extends Component { render() { return <>{this.renderCounter()}</> } renderCounter() { const { onIncrement, onDelete } = this.props return ( <div> <span className={this.getBadgeClasses()}>{this.formatCount()}</span> <button onClick={onIncrement} className="btn btn-secondary btn-sm"> Increment </button> <button onClick={onDelete} className="btn btn-danger btn-sm m-2"> Delete </button> </div> ) } getBadgeClasses() { let classes = 'badge m-2 badge-' classes += this.props.value === 0 ? 'warning' : 'primary' return classes } formatCount() { const { value } = this.props return value === 0 ? 'Zero' : value } } export default Counter
Navbar.jsx
stateを持たないコンポーネントに関しては、Stateless Functional Component (SFC)
を用いて、次のような記述をする。
Lifecycle hooksに関しても、SFC内では使用することができない。
import React from 'react' const Navbar = ({ totalCounters }) => { return ( <> <nav className="navbar navbar-light bg-light"> {/* eslint-disable-next-line jsx-a11y/anchor-is-valid */} <a className="navbar-brand" href="#"> Navbar <span className="badge badge-pill badge-info">{totalCounters}</span> </a> </nav> </> ) } export default Navbar
ちなみに、class componentについてもstateを持たないことも当然できるので、単に
Functional Component (FC)
とのみ区別した方がより適しているという議論がある。