一個前の記事のキャプチャにあるように、従来テキストリンクだった年とカテゴリーの選択をセレクトボックスにした。 git log -S
したところ去年( 2019 年)の 11 月頃に変更を行ったみたいだ。もうそろそろ 2020 年になろうとしていて、 2005 年からやっていて年の数が 16 個になろうとしていてさすがに多すぎると思ったので整理のためにセレクトボックス化してコンパクトにした。
利用したのは React Select というパッケージで、色々カスタマイズできるみたいだけど面倒だったので素のまま使ってる。
Archives ページのリファクタリング のときのように、子コンポーネントのイベントをトリガーに親コンポーネントの setState()
を呼び出すような作りになっている。作るときは結構難儀したけどおかげでスマートフォンで見たときも年やカテゴリーだけでファーストビューが埋まるということがなくなった。
現在のコードをまるっと貼り付けるとこんな感じ↓。
子コンポーネント(年のセレクトボックス)
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import Select from 'react-select'
import PropTypes from 'prop-types'
import { withRouter } from 'react-router-dom'
import history from './history'
class YearSelect extends Component {
constructor(props) {
super(props)
this.state = {
data: [],
selectedOption: null
}
this.handleChange = this.handleChange.bind(this)
}
static propTypes = {
match: PropTypes.object.isRequired,
location: PropTypes.object.isRequired,
history: PropTypes.object.isRequired
}
async loadYearSelectFromServer() {
const request = await fetch('/archives/years.json')
const response = await request.json()
this.setState({ data: response })
}
componentDidMount() {
this.loadYearSelectFromServer()
}
handleChange(selectedOption) {
const year = selectedOption ? selectedOption.value : null
if (year) {
this.props.history.push(`/archives/${year}`)
} else {
this.props.history.push("/archives")
}
this.setState(
{ selectedOption },
() => { this.props.update(year) }
)
}
render() {
const options = this.state.data.map(year => {
return { value: year, label: year }
})
return (
<div className="year-list">
<Select
value={this.state.selectedOption}
onChange={this.handleChange}
options={options}
placeholder="Year"
isClearable
/>
</div>
)
}
}
const YearList = withRouter(YearSelect)
export default YearList
親コンポーネント( App.js )
import React, { Component } from 'react'
import { BrowserRouter as Router, Switch, Route, Redirect } from 'react-router-dom'
import YearList from './YearList'
import CategoryList from './CategoryList'
import Archives from './Archives'
class App extends Component {
constructor(props) {
super(props)
this.state = {
category: null,
year: null,
length: 0
}
this.updateCategory = this.updateCategory.bind(this)
this.updateYear = this.updateYear.bind(this)
this.setLength = this.setLength.bind(this)
}
updateCategory(category) {
this.setState({ category })
}
updateYear(year) {
this.setState({ year })
}
setLength(length) {
this.setState({ length })
}
render() {
return(
<Router history={history}>
<div className="archive-filter">
<YearList update={this.updateYear} />
<CategoryList update={this.updateCategory} activeCategory={this.state.category} />
<div className="entry-length"><p>{this.state.length} entries</p></div>
</div>
<Switch>
<Route
exact path="/archives"
render={(props) =>
<Archives
category={this.state.category}
setLength={this.setLength}
{...props}
/>
}
/>
<Route
path="/archives/:year(\d{4})"
render={(props) =>
<Archives
category={this.state.category}
setLength={this.setLength}
year={this.state.year}
{...props}
/>
}
/>
</Switch>
</Router>
)
}
}
export default App