Null's Blog

React版博客系列2--分页组件设计

系列链接:

1.React版博客系列1–基础说明与标签组件
2.React版博客系列2–分页组件设计

先上demo

本系列源码: blog_react
数据服务: blog_api
线上预览: http://react.nullcn.com

接上篇

本来打算写Archive组件的,不过考虑到Archive组件跟Tags组件其实就是换汤不换药,这种如果单独开一篇来讲,这系列水就深了。
通常来说,分页除了渲染(比如页码如何显示)之外,几乎不需要什么功能,当然在考虑到分页空间一般从属于某个列表,也就是说需要当前页(必须),总页数(非必须),在放到特定的业务里,比如需要分页组件记录并使用分页前缀(~/tag/javascript)。另外,分页组件如果需要单独使用,则还需要通知上层组件,所以会有个事件通讯,当然,本博客内的分页组件具备该功能但并未使用,原因另说。先看代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
//https://github.com/mywei1989/blog_react/blob/master/assets/blog/js/Pagination.js
constructor(props){
super(props);
let pageIndex = props.pageIndex||1;
let pageCount = props.pageCount||1;
let prefix = props.prefix||'/';
this.state={
pageIndex:pageIndex,
pageCount:pageCount,
prefix:prefix
}
}

这里很好理解,定义改组件的构造函数,并默认设置了3个参数。

该方法为react内置方法,作用为”已加载组件收到新的参数时调用”,因为上层组件(列表)如果因为某种原因触发了render,则可能调用该方法,重置3个默认值。这里顺带提一句,props与state的区别:由于props和state都用于描述组件的特性,可能会产生混淆。一个简单的区分方法是,props表示那些一旦定义,就不再改变的特性,而state是会随着用户互动而产生变化的特性。在调用setState时,react会自动触发render事件。

1
2
3
4
5
6
7
8
//https://github.com/mywei1989/blog_react/blob/master/assets/blog/js/Pagination.js
componentWillReceiveProps(nextProps){
this.setState({
pageIndex:nextProps.pageIndex,
pageCount:nextProps.pageCount,
prefix:nextProps.prefix
});
}

通知上层组件的接口

1
2
3
_handleChange(pageIndex){
this.props.onChange(pageIndex);
}

最后是render方法,该方法这么多完全是我想实现一套自己的展示方式,即根据pageCount与pageIndex不同,根据自己需要的逻辑渲染出不一样的分页效果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
render(){
let pageHtml=[];
if(this.props.pageIndex>1){
if(this.props.pageIndex-1===1){
pageHtml.push(<a className="extend prev" rel="prev" href={this.props.prefix+"/"} key="«">«</a>);
}else{
pageHtml.push(<a className="extend prev" rel="prev" href={this.props.prefix+"/page/"+(this.props.pageIndex-1)+"/"} key="«">«</a>);
}
}
if(this.props.pageIndex===2){
pageHtml.push(<a className="page-number" href={this.props.prefix+"/"} key="1">1</a>);
}else if(this.props.pageIndex===3){
pageHtml.push(<a className="page-number" href={this.props.prefix+"/"} key="1">1</a>);
pageHtml.push(<a className="page-number" href={this.props.prefix+"/page/2/"} key="2">2</a>);
}else if(this.props.pageIndex===4){
pageHtml.push(<a className="page-number" href="/" key="1">1</a>);
pageHtml.push(<a className="page-number" href={this.props.prefix+"/page/2/" } key="2">2</a>);
pageHtml.push(<a className="page-number" href={this.props.prefix+"/page/3/" } key="3">3</a>);
}else if(this.props.pageIndex===5){
pageHtml.push(<a className="page-number" href="/" key="1">1</a>);
pageHtml.push(<span className="space" key="..."></span>);
for(let i=this.props.pageIndex-2;i<this.props.pageIndex;i++){
pageHtml.push(<a className="page-number" href={this.props.prefix+"/page/"+i+"/" } key={i}>{i}</a>);
}
}
pageHtml.push(<span className="page-number current" key={this.props.pageIndex} key={this.props.pageIndex}>{this.props.pageIndex}</span>);
if(this.props.pageCount - this.props.pageIndex >= 4){
pageHtml.push(<a className="page-number" href={this.props.prefix+"/page/"+(this.props.pageIndex+1)+"/"} key={this.props.pageIndex+1} key={this.props.pageIndex+1} >{this.props.pageIndex+1}</a>);
pageHtml.push(<a className="page-number" href={this.props.prefix+"/page/"+(this.props.pageIndex+2)+"/"} key={this.props.pageIndex+2} key={this.props.pageIndex+2} >{this.props.pageIndex+2}</a>);
pageHtml.push(<span className="space" key="..."></span>);
pageHtml.push(<a className="page-number" href={this.props.prefix+"/page/"+this.props.pageCount+"/"} key={this.props.pageCount}>{this.props.pageCount}</a>);
}else{
for(let i=this.props.pageIndex+1;i<=this.props.pageCount;i++){
pageHtml.push(<a className="page-number" href={this.props.prefix+"/page/"+i+"/"} key={i}>{i}</a>);
}
}
if(this.props.pageIndex!==this.props.pageCount){
pageHtml.push(<a className="extend next" rel="next" href={this.props.prefix+"/page/"+(this.props.pageIndex+1)+"/"} key="»">»</a>);
}
return(
<div className="archive-pagination">
<div className="paginator">
{pageHtml}
</div>
</div>
)
}

render方法里为何没有使用_handleChange方法?

因为出于全局的考虑,并不打算通知上层翻页,而是通过更上层的react-router方式,这里附上使用通知上层的方法与调用方式:

1
2
3
4
5
6
7
8
9
10
//上层组件
<Pagination onChange={this.onChange} pageIndex={this.state.pageIndex} pageCount={this.state.pageCount} prefix={this.state.prefix} />
//Pagination.js
render(){
return (
<a className="page-number" href="#" onClick={this._handleChange(3)}>3</a>
);
}

当然,我们还可以扩展一下,比如上一页:

1
2
3
4
5
6
7
_prev(){
if(this.state.pageIndex>1){
this._handleChange(this.state.pageIndex-1);
}else{
this._handleChange(1);
}
}

如果想定制不同的风格,比如分页省略号的位置等等,可以使用上层组件,通过props方式传递给分页组件。
当然,分页组件也不限定使用a标签,其他的标签一样可以。

下篇预告

下篇的内容将会是引入react-router,并对react-router做一些必要的说明,可能是本系列最重要的一篇,基于react-router将该博客构建成SPA的页面。之后,会改造工程结构,开发Article(文章)组件并将他们组合起来。
现在可以直接看看目前本人react博客的线上效果:
http://react.nullcn.com