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 内に その変数が 格納されます
この格納されている 変数をもとに 自分が どこにいるのかの 判断になります
随分雑な記事になってしまいましたが わからないところ 不明なところがありましたら コメントお願いします