概要
semantic-ui-react
を用いてページネーションの機能を実装していたのですが、
ページネーションの番号とURLが一意に対応するように実装をしてみました。
これによって、例えば戻るボタンで別ページからページネーションのページに戻った際に
常にデフォルトで設定されてるページ番号に遷移してしまう現象を防げます。
実装
まずはページネーションで扱う変数を定義します。
List.tsx
interface ArticleState {
articleFromApi: Article[];
articleToDisplay: Article[];
begin: number;
end: number;
activePage: number;
redirect: boolean;
}
-
articleFromApi
-> apiから取得した全データ -
articleToDisplay
->articleFromApi
からページネーションで表示するデータだけ抽出したもの -
begin
->articleFromApi
からarticleToDisplay
に抽出する初めのデータの数字を記録 -
end
->articleFromApi
からarticleToDisplay
に抽出する最後のデータの数字を記録 -
activePage
-> クリックされたorされているページネーション番号を記憶しておく変数
コンストラクタを設定します
List.tsx
constructor(props: RouteComponentProps<{id: string}>) {
super(props);
this.state = {
articleFromApi: [],
articleToDisplay: [],
begin: 0,
end: 5,
activePage: 1,
redirect: false,
};
this.getArticle = this.getArticle.bind(this);
this.pageChange = this.pageChange.bind(this);
this.setList = this.setList.bind(this);
this.setActiveList = this.setActiveList.bind(this);
}
- デフォルトではページネーション先頭の「1」番目を開くように
activePage
:1
と設定しています。 - 1ページにつき5つのデータが表示されるようにしているので、初期の
start
とend
はそれぞれ0
,5
です。
List.tsx
async pageChange(
event: React.MouseEvent<HTMLAnchorElement>,
data: PaginationProps
) {
await this.setState({activePage: data.activePage as number});
await this.setActiveList();
this.setState({redirect: true});
}
async setActiveList() {
await this.setState({begin: this.state.activePage * 5 - 5});
await this.setState({end: this.state.activePage * 5});
this.setState(
{
articleToDisplay: this.state.articleFromApi.slice(
this.state.begin,
this.state.end
),
},
() => window.scrollTo(0, 0)
);
}
async componentDidMount() {
this.setState({articleFromApi: []});
await this.getArticle();
await this.setState({activePage: Number(this.props.match.params.id)});
this.setActiveList();
}
- 直接URLを叩いた際は
componentDidMount()
<-setActiveList()
の順で参照されて、articleToDisplay
が形成されます。 - ページネーションコンポーネントがクリックされた際は
pageChange()
<-setActiveList()
の順で参照されて、articleToDisplay
が形成されます。 - いずれの場合もやっていることは、ページネーション番号から表示するデータを抽出する作業です。
最後にコンポーネントをrenderします。
List.tsx
render() {
return (
<Container style={{marginTop: '3em'}} text>
<Grid columns={1} divided="vertically">
<Grid.Row>
{(this.state.articleToDisplay || []).map(function(articleData, i) {
return (
<Grid.Column>
<Segment>
<Header as="h1">{articleData.title}</Header>
<p style={{textOverflow: 'clip', wordBreak: 'break-all'}}>
{articleData.content.length > 100
? articleData.content.substring(0, 97) + '...'
: articleData.content}
</p>
<Link to={`/detail/${articleData.id}`}>
continue reading
</Link>
</Segment>
</Grid.Column>
);
})}
</Grid.Row>
</Grid>
<Pagination
defaultActivePage={this.props.match.params.id}
totalPages={Math.ceil(this.state.articleFromApi.length / 5)}
onPageChange={this.pageChange}
/>
{this.renderRedirect()}
</Container>
);
}
-
Grid
内でarticleToDisplay
を描写しています。 -
Pagination
内でページネーションの諸々を設定しています。defaultActivePage
はURLのパラーメーターと一致するように設定しています。totalPages
は1ページあたり5つのデータに対応するように設定しています。
また、URLのパラメーターとページネーション番号が一致するようにApp.tsxを設定します。
App.tsx
<Route exact path="/:id" component={List} />
List.tsx
export default withRouter(List);
ソースコード(全体)
以下のレポジトリに全貌のソースコードがあります。
https://github.com/jpskgc/article/blob/master/client/src/components/List.tsx