React を 触ってみた (8) – 記事一覧 を 表示
この記事は 2018年 5月 4日 に書かれた記事です。
今回は Hello World と 表示されている 本文部分に 記事を 表示させていきたいと思います
前回の記事
React を 触ってみた (7) – 再帰処理 で全ての Taxonomy を 取得する
https://tekuaru.jack-russell.jp/2018/05/03/1810/
投稿 や 投稿一覧 を Material-UI の Card で 表示
タクソノミーの表示 と 流れは一緒なので
今回は サクッと Material-UI の Card を 使って 表示させていきたいと思います
/src/component-parts/card-posts.jsx
import React from 'react'
import { Link } from 'react-router-dom'
import PropTypes from 'prop-types'
import { withStyles } from 'material-ui/styles'
import Button from 'material-ui/Button'
import Card, { CardActions, CardContent } from 'material-ui/Card'
import Typography from 'material-ui/Typography'
import { connect } from 'react-redux'
import { mapStateToProps, mapDispatchToProps } from '../redux/containers'
const styles = {
card: {
marginBottom: 20,
},
}
class CardPosts extends React.Component {
constructor (props) {
super(props)
}
componentWillMount() {
console.log( 'componentWillMount' )
this.props.handleLoadPosts( this.props )
}
componentWillReceiveProps(nextProps) {
if(nextProps.location !== this.props.location) {
console.log( 'componentWillReceiveProps' )
this.props.handleLoadPosts( nextProps )
}
}
render() {
const { classes, posts } = this.props
var card = ''
if( Object.prototype.toString.call(this.props.posts) != '[object Array]' ) {
var item = this.props.posts
card = (
<section>
<Card className={classes.card}>
<CardContent>
<Typography variant="headline" component="h2">
{item.title.rendered}
</Typography>
<Typography component="div" dangerouslySetInnerHTML={{__html: item.content.rendered}} />
</CardContent>
</Card>
</section>
)
} else {
card = (
<section>
{this.props.posts.map((item) => {
return (
<Card key={item.id} className={classes.card}>
<CardContent>
<Typography variant="headline" component="h2">
{item.title.rendered}
</Typography>
</CardContent>
<CardActions>
<Button size="small" component={Link} to={`/posts/${item.id}`}>Learn More</Button>
</CardActions>
</Card>
)
})}
</section>
)
}
return card
}
}
CardPosts.propTypes = {
classes: PropTypes.object.isRequired,
}
export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(CardPosts))
render 内で 投稿用の表示と 投稿一覧用の表示を 分けていますが
本当なら ここも コンポーネントの読み出し時に 分けるのが良いのでしょうが 今回は 一緒にしています
/src/component/main.jsx : 一部
import { Switch, Route } from 'react-router-dom'
import CardPosts from '../component-parts/card-posts'
Switch, Route と CardPosts を 新たに 読み込みます
<h1>Hello World</h1>
と 書いていたコード は
<Switch>
<Route exact path="/" component={CardPosts} />
<Route exact path="/:taxonomy/:id/" component={CardPosts} />
<Route exact path="/:taxonomy/:id/:page/" component={CardPosts} />
</Switch>
に 変更します
ココのポイントは Switch や exact で 条件を満たす者だけの コンポーネントを 選択している所です
Redux 関連
ここは そろそろ 感覚的に掴んできたのではないかと思いますので かなり ざっくりと コードを 載せていきます
/src/redux/containers.jsx : 一部
import { toggleDrawer, loadPosts, loadTaxonomies } from './actions'
export function mapDispatchToProps(dispatch) {
return {
/* 以前の記述 */
handleLoadPosts: (props) => { dispatch( loadPosts(props) ) },
}
}
/src/redux/actions.jsx : 一部
export function loadPosts(props, page = 1) {
return function(dispatch) {
var url = props.API.default + props.API.namespace
if( Object.keys(props.match.params).length !== 0 ) {
switch(props.match.params.taxonomy) {
case 'posts':
url += 'posts' + '/' + props.match.params.id + '/'
break
case 'pages':
url += 'pages' + '/' + props.match.params.id + '/'
break
default:
url += 'posts' + '/' + '?' + props.match.params.taxonomy + '=' + props.match.params.id
}
} else {
url += 'posts' + '/'
}
fetch(url)
.then(response => response.json())
.then(result => dispatch(setPosts(result, props.match.params.taxonomy, page)))
.catch(error => console.log(error))
// end fetch
}
}
export function setPosts(result, taxonomy = 'posts', page) {
return {
type: 'SET-POSTS',
result,
taxonomy,
page,
}
}
/src/redux/reducers.jsx : case のみ
const initialState = {
/* 以前の記述 */
posts: [],
}
export default function reducer(state = initialState, action) {
switch(action.type) {
case 'TOGGLE-DRAWER':
console.log( 'mobileOpen : ' + !state.mobileOpen )
return Object.assign({}, state, {
mobileOpen: !state.mobileOpen,
})
case 'SET-POSTS':
console.log( action.taxonomy + ' : Page : ' + action.page )
console.log( action.result )
return Object.assign({}, state, {
posts: action.result,
})
case 'SET-TAXONOMIES':
console.log( action.taxonomy + ' : Page : ' + action.page )
console.log( action.result )
return Object.assign({}, state, {
[action.taxonomy]: ( action.page == 1 ) ? action.result : state[action.taxonomy].concat(action.result),
})
default:
return state
}
}
という感じで 処理を 追記します
ココらへんは 前回の記事 を 参考に ちょっと 修正をすれば良い感じですかね
まとめ
今回のポイントは Route の path です
補足で 説明していませんでしたが :hoge とか :huga と書くと props.match.params 内に その変数が 格納されます
この格納されている 変数をもとに 自分が どこにいるのかの 判断になります
随分雑な記事になってしまいましたが わからないところ 不明なところがありましたら コメントお願いします