完整版下载地址:https://gitee.com/dgx/DiReactPagination
完整版演示地址:http://dgx.gitee.io/direactpagination/build/index.html#/
一.准备工作
我们是继续基于这个demo项目实现一个分页插件,这个分页插件会用在各个需要的组建当中。
分页插件会用在任何一个可能的位置,我们在tpl目录创建这个组件:DiReactPage
DiReactPage.js:
我们知道这个组件是通用的,他回以子组件的方式应用到需要的位置,一般一个分页插件包含:
1.第一页按钮
2.上一页按钮(在第一页无效)
3.页数按钮(显现当前位置以及之后的5个,如果总页数小于5特殊处理)
4.下一页按钮(在第最后一页无效)
5.最后一页按钮
6.总页数
7.跳页操作(一个输入框,后面一个点击按钮)
我们先考虑逻辑处理,先完成结构和样式,实现一个可视化的静态分页。
二.静态实现
DiReactPage.js使用jsx书写结构,我们的分页样式为了简单化都会提到一个共用位置,在index.html去引入DiReactPage.css。
DiReactPage.js:
import React,{ Component } from 'react'; //=====分页组件===== class DiReactPage extends Component { render() { return ( <div className="DiReactPage"> <div className="DiReactPage-btn">第一页</div> <div className="DiReactPage-btn disable">上一页</div> <div className="DiReactPage-page"> <span className="active">1</span> <span>2</span> <span>3</span> <span>4</span> </div> <div className="DiReactPage-btn">下一页</div> <div className="DiReactPage-btn">最后一页</div> <div className="DiReactPage-btn">总4页</div> <input className="DiReactPage-btn" type="text" /> <button className="DiReactPage-btn">跳转</button> </div> ); } componentDidMount() { console.log("我是分页组件") } } export default DiReactPage
会有两个特殊的类名,一个是disable用来标记按钮是否可用,active标签当前所在的页数。
index.html:
<!doctype html> <html lang="en"> <head> <Meta charset="utf-8"> <Meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"> <Meta name="theme-color" content="#000000"> <title>React App</title> <link rel="stylesheet" href="css/DiReactPage.css" /> </head> <body> <div id="root"></div> </body> </html>
DiReactPage.css:
.DiReactPage{ height:30px; line-height:30px; text-align:center;} .DiReactPage .DiReactPage-btn{ display:inline-block; height:30px; line-height:30px; padding:0 5px; margin:0 5px; border-radius:4px; background:#09F; cursor:pointer;} .DiReactPage .DiReactPage-btn.disable{ background:#999;cursor:not-allowed;} .DiReactPage .DiReactPage-page{ display:inline-block; height:30px; line-height:30px; margin:0 20px;} .DiReactPage .DiReactPage-page span{display:inline-block; height:30px; line-height:30px; padding:0 5px; margin:0 5px; color:#000; cursor:pointer;} .DiReactPage .DiReactPage-page span.active{ color:#09F; } .DiReactPage .iReactPage-input{ width:100px; border:1px solid #666; border-radius:4px;height:30px; line-height:30px; } .DiReactPage .DiReactPage-skip{display:inline-block; height:30px; line-height:30px; width:80px; border-radius:4px; background:#09F;cursor:pointer; border:none; margin:0 10px;}
News.js:
import React,{ Component } from 'react'; import {Redirect} from 'react-router-dom'; import DiReactPage from '../../tpl/DiReactPage.js'; //=====组件===== class News extends Component { constructor(props) { super(props); } render() { if(this.props.isLogin==false){ return <Redirect to="/" /> } return ( <div> <h3>新闻页面</h3> <ul> { this.props.list.map(function(item,i){ return <li key={item.id}> <a>{item.title}</a> <span>{item.con}</span> </li> }) } </ul> <DiReactPage /> <button onClick={this.SORTREVERSE.bind(this)}>倒叙显示</button> </div> ); } SORTREVERSE(){ this.props.SORTREVERSE(); } componentDidMount() { console.log("News渲染完毕") } } export default News
三.逻辑分析和准备工作
1.我们知道组件之间可以传递数据,利用属性,
2.我们的分页json是存放在redux中
3.我们会有一个action,会根据当前点击的页数返回对应的json数据
4.分页的显示状态可以完全来自父组件,也就是总页数,当前第几页等都是父组件作为属性扔给分页组件
5.发射action(获取对应页数的数据)方法也要放在父组件当中,因为分页组件写死就变成只能修改一个redux文件,所以这个方法也要传递给分页组件
6.总结得出分页组件只是承受的一端,一切操作和显示都要来自父组件
我们先给news相关的处理加入分页功能,包含一个返回对应页数的acion,一个数据返回相关信息的模拟,
包含总页数,当前的页数,是否有下一页状态的ajax信息等!
PageShow.js:
import React,{ Component } from 'react'; import {Redirect} from 'react-router-dom'; import DiReactPage from '../../tpl/DiReactPage.js'; //=====组件===== class PageShow extends Component { constructor(props) { super(props); } render() { if(this.props.isLogin==false){ return <Redirect to="/" /> } return ( <div> <h3>分页展示</h3> <ul> { this.props.list.map(function(item,i){ return <li key={item.id}> <span>{item.text}</span> </li> }) } </ul> <DiReactPage /> </div> ); } componentDidMount() { console.log("PageShow渲染完毕"); this.props.GETLIST(2); } } export default PageShow
PageShowReactRedux.js:
import { connect } from 'react-redux'; //=====引入组件===== import PageShow from './PageShow.js' //=====react-redux 封装组件===== // 哪些 Redux 全局的 state 是我们组件想要通过 props 获取的? function mapStateToProps(state) { return { isLogin:state.isLogin,list: state.PageShowRedux.list,allpage: state.PageShowRedux.allpage,currentpage: state.PageShowRedux.currentpage,nextpage: state.PageShowRedux.nextpage }; } // 哪些 action 创建函数是我们想要通过 props 获取的? function mapDispatchToProps(dispatch) { return { GETLIST:function(currentpage){ var list=[]; var allpage=""; var nextpage=""; setTimeout(function(){ //模拟ajax数据 if(currentpage==1){ list=[ {id:1,text:"111111"},{id:2,text:"222222"},{id:3,text:"3333333333"},{id:4,text:"44444444444"},{id:5,text:"555555555"},] allpage=7 nextpage=true; }else if(currentpage==2){ list=[ {id:1,text:"66666666"},text:"7777777777"},text:"8888888888"},text:"99999999999"},text:"101010"},] allpage=7 nextpage=true; }else if(currentpage==3){ list=[ {id:1,text:"111111111111111"},text:"121212"},text:"131313"},text:"141414"},text:"15515"},] allpage=7 nextpage=true; }else if(currentpage==4){ list=[ {id:1,text:"161616"},text:"171717"},text:"181818"},text:"191919"},text:"202020"},] allpage=7 nextpage=true; }else if(currentpage==5){ list=[ {id:1,text:"2121"},text:"22222"},text:"232323"},text:"242424"},text:"252525"},] allpage=7 nextpage=true; }else if(currentpage==6){ list=[ {id:1,text:"2626"},text:"2727"},text:"2828"},text:"2929"},text:"3030"},] allpage=7 nextpage=true; }else if(currentpage==7){ list=[ {id:1,text:"3131"},text:"3232"} ] allpage=7 nextpage=false; }; dispatch({type:"GET_LIST",list:list,allpage:allpage,currentpage:currentpage,nextpage:nextpage}) },1000); } }; } //封装传递state和dispatch var PageShowReactRedux = connect(mapStateToProps,mapDispatchToProps)(PageShow); export default PageShowReactRedux
PageShowRedux.js:
//reducer var pageinit={ list:[],allpage:0,currentpage:1,nextpage:false }; function PageShowRedux(state = pageinit,action) { switch (action.type) { case "GET_LIST": //返回当前对应页数的信息 return Object.assign({},state,{ list:action.list,allpage:action.allpage,currentpage:action.currentpage,nextpage:action.nextpage }) default: return state } } export default PageShowRedux
路由添加相关
rootRedux.js:
import { combineReducers } from 'redux'; //全局reducer import isLogin from './indexRedux.js' //子reducer import NewsRedux from './page/news/NewsRedux.js' import PageShowRedux from './page/pageshow/PageShowRedux.js' //合并reducer var rootRedux = combineReducers({ isLogin,NewsRedux,PageShowRedux }) export default rootRedux
App.js:
import React,{ Component } from 'react'; import { Route,Link,Switch } from 'react-router-dom'; import NavReactRedux from './NavReactRedux.js'; import LoginReactRedux from './page/login/LoginReactRedux.js'; import HomeReactRedux from './page/home/HomeReactRedux.js'; import AboutReactRedux from './page/about/AboutReactRedux.js'; import NewsReactRedux from './page/news/NewsReactRedux.js'; import PageShowReactRedux from './page/pageshow/PageShowReactRedux.js'; import NotFind from './page/notFind/NotFind.js'; class App extends Component { render() { return ( <div className="App"> <NavReactRedux /> <div> <Switch> <Route exact path="/" component={LoginReactRedux}/> <Route exact path="/Home" component={HomeReactRedux}/> <Route path="/About" component={AboutReactRedux}/> <Route exact path="/News" component={NewsReactRedux}/> <Route exact path="/PageShow" component={PageShowReactRedux}/> <Route component={NotFind}/> </Switch> </div> </div> ); } } export default App;
Nav.js:
import React,Link } from 'react-router-dom'; class Nav extends Component { render() { return ( <ul style={{display:this.props.isLogin?"block":"none"}}> <li style={{display:this.props.isLogin?"none":"block"}}> <Link to="/">登录</Link> </li> <li> <Link to="/Home">主页</Link> </li> <li> <Link to="/About">关于我们</Link> </li> <li> <Link to="/News">新闻页面</Link> </li> <li> <Link to="/PageShow">分页演示</Link> </li> </ul> ); } } export default Nav;
我们参数传递了2所以模拟返回了第二页数据!
四.显示效果逻辑书写
默认传递是第一页:
总页数
当前页数
是否有下一页
PageShow.js:
import React,i){ return <li key={item.id}> <span>{item.text}</span> </li> }) } </ul> <DiReactPage allpage={this.props.allpage} currentpage={this.props.currentpage} nextpage={this.props.nextpage} /> </div> ); } componentDidMount() { console.log("PageShow渲染完毕"); this.props.GETLIST(5); } } export default PageShow
DiReactPage.js:
import React,{ Component } from 'react'; //=====分页组件===== class DiReactPage extends Component { constructor(props) { super(props); } render() { //显示控制 var currentpage=this.props.currentpage; var pages=[]; if(this.props.allpage>=5){ if(currentpage+5>this.props.allpage){ for(var i=currentpage;i<=this.props.allpage;i++){ pages.push(i); }; }else{ for(var i=currentpage;i<currentpage+5;i++){ pages.push(i); }; }; }else{ for(var i=currentpage;i<=this.props.allpage;i++){ pages.push(i); }; }; return ( <div className="DiReactPage"> <div className="DiReactPage-btn">第一页</div> <div className={this.props.currentpage==1?"DiReactPage-btn disable":"DiReactPage-btn"}>上一页</div> <div className="DiReactPage-page"> { pages.map(function(item,i){ return <span key={i} className={currentpage==item?"active":""}>{item}</span> }) } </div> <div className={this.props.currentpage==this.props.allpage?"DiReactPage-btn disable":"DiReactPage-btn"}>下一页</div> <div className="DiReactPage-btn">最后一页</div> <div className="DiReactPage-btn">总{this.props.allpage}页</div> <input className="DiReactPage-input" type="text" /> <button className="DiReactPage-skip">跳转</button> </div> ); } componentDidMount() { console.log("我是分页组件"); } } export default DiReactPage
五.操作效果逻辑书写
就是各种点击事件的处理,在分页中,我们可以加入click事件即可。
DiReactPage.js:
import React,{ Component } from 'react'; //=====分页组件===== class DiReactPage extends Component { constructor(props) { super(props); } render() { //显示控制 var currentpage=this.props.currentpage; var pages=[]; if(this.props.allpage>=5){ if(currentpage+5>this.props.allpage){ for(var i=currentpage;i<=this.props.allpage;i++){ pages.push(i); }; }else{ for(var i=currentpage;i<currentpage+5;i++){ pages.push(i); }; }; }else{ for(var i=currentpage;i<=this.props.allpage;i++){ pages.push(i); }; }; return ( <div className="DiReactPage"> <div className="DiReactPage-btn" onClick={this.diFirst.bind(this)}>第一页</div> <div className={this.props.currentpage==1?"DiReactPage-btn disable":"DiReactPage-btn"} onClick={this.diPrev.bind(this)}>上一页</div> <div className="DiReactPage-page"> { pages.map(function(item,i){ return <span key={i} className={currentpage==item?"active":""} onClick={this.diCurrent.bind(this,item)}>{item}</span> }.bind(this)) } </div> <div className={this.props.currentpage==this.props.allpage?"DiReactPage-btn disable":"DiReactPage-btn"} onClick={this.diNext.bind(this)}>下一页</div> <div className="DiReactPage-btn" onClick={this.diLast.bind(this)}>最后一页</div> <div className="DiReactPage-btn">总{this.props.allpage}页</div> <input className="DiReactPage-input" type="text" ref="cPage" /> <button className="DiReactPage-skip" onClick={this.diSkip.bind(this)}>跳转</button> </div> ); } diFirst(){ alert(1) } diPrev(){ alert(this.props.currentpage-1) } diCurrent(page){ alert(page) } diNext(){ alert(this.props.currentpage+1) } diLast(){ alert(this.props.allpage) } diSkip(){ alert(this.refs.cPage.value) } componentDidMount() { console.log("我是分页组件"); } } export default DiReactPage
我们分析一部分已经说明,我们属性已经获取到了,还差最后一个就是把父组件获取数据的方法作为属性也传递给分页组件调用即可,不过在上一页和下一页按钮我们要有一个逻辑显示限制,就是在第一页和最后一页是不可点击的状态。
跳页处理非常简单我们把inpput的value拿到传递即可!
DiReactPage.js:
import React,{ Component } from 'react'; //=====分页组件===== class DiReactPage extends Component { constructor(props) { super(props); } render() { //显示控制 var currentpage=this.props.currentpage; var pages=[]; if(this.props.allpage>=5){ if(currentpage+5>this.props.allpage){ for(var i=currentpage;i<=this.props.allpage;i++){ pages.push(i); }; }else{ for(var i=currentpage;i<currentpage+5;i++){ pages.push(i); }; }; }else{ for(var i=currentpage;i<=this.props.allpage;i++){ pages.push(i); }; }; return ( <div className="DiReactPage"> <div className="DiReactPage-btn" onClick={this.diFirst.bind(this)}>第一页</div> <div className={this.props.currentpage==1?"DiReactPage-btn disable":"DiReactPage-btn"} onClick={this.diPrev.bind(this)}>上一页</div> <div className="DiReactPage-page"> { pages.map(function(item,item)}>{item}</span> }.bind(this)) } </div> <div className={this.props.currentpage==this.props.allpage?"DiReactPage-btn disable":"DiReactPage-btn"} onClick={this.diNext.bind(this)}>下一页</div> <div className="DiReactPage-btn" onClick={this.diLast.bind(this)}>最后一页</div> <div className="DiReactPage-btn">总{this.props.allpage}页</div> <input className="DiReactPage-input" type="text" ref="cPage" /> <button className="DiReactPage-skip" onClick={this.diSkip.bind(this)}>跳转</button> </div> ); } diFirst(){ //alert(1) this.props.GETLIST(1); } diPrev(){ //alert(this.props.currentpage-1) if(this.props.currentpage-1<1){ return false; } this.props.GETLIST(this.props.currentpage-1); } diCurrent(page){ //alert(page) this.props.GETLIST(page); } diNext(){ //alert(this.props.currentpage+1) if(this.props.currentpage+1>this.props.allpage){ return false; } this.props.GETLIST(this.props.currentpage+1); } diLast(){ //alert(this.props.allpage) this.props.GETLIST(this.props.allpage); } diSkip(){ //alert(this.refs.cPage.value) this.props.GETLIST(this.refs.cPage.value); } componentDidMount() { console.log("我是分页组件"); } } export default DiReactPage
到这一步我们大致的处理就基本完成了!
六.一些处理的简单优化
1.ajax模拟返回优化
首先我们模拟的时间是1秒返回结果,我们改成200毫秒,更接近实际的ajax:
PageShowReactRedux.js:
import { connect } from 'react-redux'; //=====引入组件===== import PageShow from './PageShow.js' //=====react-redux 封装组件===== // 哪些 Redux 全局的 state 是我们组件想要通过 props 获取的? function mapStateToProps(state) { return { isLogin:state.isLogin,200); } }; } //封装传递state和dispatch var PageShowReactRedux = connect(mapStateToProps,mapDispatchToProps)(PageShow); export default PageShowReactRedux
2.有无数据提示优化
在没有数据的时候我们要提示没有数据:
显示成这样:
我们给分页组件和结构加入一个style是否显示判断就可以了:
PageShow.js:
import React,{ Component } from 'react'; import {Redirect} from 'react-router-dom'; import DiReactPage from '../../tpl/DiReactPage.js'; //=====组件===== class PageShow extends Component { constructor(props) { super(props); } render() { if(this.props.isLogin==false){ return <Redirect to="/" /> } return ( <div> <h3>分页展示</h3> <ul style={{display:this.props.allpage<=0?"none":"block"}}> { this.props.list.map(function(item,i){ return <li key={item.id}> <span>{item.text}</span> </li> }) } </ul> <div style={{display:this.props.allpage<=0?"block":"none"}}>没有数据了</div> <DiReactPage allpage={this.props.allpage} currentpage={this.props.currentpage} nextpage={this.props.nextpage} GETLIST={this.props.GETLIST}/> </div> ); } componentDidMount() { console.log("PageShow渲染完毕"); this.props.GETLIST(1); } } export default PageShow
DiReactPage.js:
import React,{ Component } from 'react'; //=====分页组件===== class DiReactPage extends Component { constructor(props) { super(props); } render() { //显示控制 var currentpage=this.props.currentpage; var pages=[]; if(this.props.allpage>=5){ if(currentpage+5>this.props.allpage){ for(var i=currentpage;i<=this.props.allpage;i++){ pages.push(i); }; }else{ for(var i=currentpage;i<currentpage+5;i++){ pages.push(i); }; }; }else{ for(var i=currentpage;i<=this.props.allpage;i++){ pages.push(i); }; }; return ( <div className="DiReactPage" style={{display:this.props.allpage<=0?"none":"block"}}> <div className="DiReactPage-btn" onClick={this.diFirst.bind(this)}>第一页</div> <div className={this.props.currentpage==1?"DiReactPage-btn disable":"DiReactPage-btn"} onClick={this.diPrev.bind(this)}>上一页</div> <div className="DiReactPage-page"> { pages.map(function(item,item)}>{item}</span> }.bind(this)) } </div> <div className={this.props.currentpage==this.props.allpage?"DiReactPage-btn disable":"DiReactPage-btn"} onClick={this.diNext.bind(this)}>下一页</div> <div className="DiReactPage-btn" onClick={this.diLast.bind(this)}>最后一页</div> <div className="DiReactPage-btn">总{this.props.allpage}页</div> <input className="DiReactPage-input" type="text" ref="cPage" /> <button className="DiReactPage-skip" onClick={this.diSkip.bind(this)}>跳转</button> </div> ); } diFirst(){ //alert(1) this.props.GETLIST(1); } diPrev(){ //alert(this.props.currentpage-1) if(this.props.currentpage-1<1){ return false; } this.props.GETLIST(this.props.currentpage-1); } diCurrent(page){ //alert(page) this.props.GETLIST(page); } diNext(){ //alert(this.props.currentpage+1) if(this.props.currentpage+1>this.props.allpage){ return false; } this.props.GETLIST(this.props.currentpage+1); } diLast(){ //alert(this.props.allpage) this.props.GETLIST(this.props.allpage); } diSkip(){ //alert(this.refs.cPage.value) this.props.GETLIST(this.refs.cPage.value); } componentDidMount() { console.log("我是分页组件"); } } export default DiReactPage
3.测试分页显示逻辑,总页数小于5
我们只需要修改PageShowReactRedux.js:
只有4页数据
import { connect } from 'react-redux'; //=====引入组件===== import PageShow from './PageShow.js' //=====react-redux 封装组件===== // 哪些 Redux 全局的 state 是我们组件想要通过 props 获取的? function mapStateToProps(state) { return { isLogin:state.isLogin,] allpage=4 nextpage=true; }else if(currentpage==2){ list=[ {id:1,] allpage=4 nextpage=true; }else if(currentpage==3){ list=[ {id:1,] allpage=4 nextpage=true; }else if(currentpage==4){ list=[ {id:1,text:"181818"} ] allpage=4 nextpage=false; } dispatch({type:"GET_LIST",mapDispatchToProps)(PageShow); export default PageShowReactRedux
效果如下:
4.跳页功能处理,阻止溢出bug
我们总页数是7,如果用户输入1-7之外的数组或者输入汉字和字母我们应该提示用户输入数据异常,不执行跳转:
DiReactPage.js:
import React,item)}>{item}</span> }.bind(this)) } </div> <div className={this.props.currentpage==this.props.allpage?"DiReactPage-btn disable":"DiReactPage-btn"} onClick={this.diNext.bind(this)}>下一页</div> <div className="DiReactPage-btn" onClick={this.diLast.bind(this)}>最后一页</div> <div className="DiReactPage-btn">总{this.props.allpage}页</div> <input className="DiReactPage-input" type="text" ref="cPage" /> <button className="DiReactPage-skip" onClick={this.diSkip.bind(this)}>跳转</button> </div> ); } diFirst(){ //alert(1) this.props.GETLIST(1); } diPrev(){ //alert(this.props.currentpage-1) if(this.props.currentpage-1<1){ return false; } this.props.GETLIST(this.props.currentpage-1); } diCurrent(page){ //alert(page) this.props.GETLIST(page); } diNext(){ //alert(this.props.currentpage+1) if(this.props.currentpage+1>this.props.allpage){ return false; } this.props.GETLIST(this.props.currentpage+1); } diLast(){ //alert(this.props.allpage) this.props.GETLIST(this.props.allpage); } diSkip(){ //alert(this.refs.cPage.value) if(isNaN(this.refs.cPage.value)){ console.log("必须是数字") return false; } if(this.refs.cPage.value<1 || this.refs.cPage.value>this.props.allpage){ console.log("超过范围") return false; } this.props.GETLIST(this.refs.cPage.value); } componentDidMount() { console.log("我是分页组件"); } } export default DiReactPage
七.总结
我们这时候一个简单的分页组件就封装完毕。
DiReactPage.js:
import React,item)}>{item}</span> }.bind(this)) } </div> <div className={this.props.currentpage==this.props.allpage?"DiReactPage-btn disable":"DiReactPage-btn"} onClick={this.diNext.bind(this)}>下一页</div> <div className="DiReactPage-btn" onClick={this.diLast.bind(this)}>最后一页</div> <div className="DiReactPage-btn">总{this.props.allpage}页</div> <input className="DiReactPage-input" type="text" ref="cPage" /> <button className="DiReactPage-skip" onClick={this.diSkip.bind(this)}>跳转</button> </div> ); } diFirst(){ //alert(1) this.props.GETLIST(1); } diPrev(){ //alert(this.props.currentpage-1) if(this.props.currentpage-1<1){ return false; } this.props.GETLIST(this.props.currentpage-1); } diCurrent(page){ //alert(page) this.props.GETLIST(page); } diNext(){ //alert(this.props.currentpage+1) if(this.props.currentpage+1>this.props.allpage){ return false; } this.props.GETLIST(this.props.currentpage+1); } diLast(){ //alert(this.props.allpage) this.props.GETLIST(this.props.allpage); } diSkip(){ //alert(this.refs.cPage.value) if(isNaN(this.refs.cPage.value)){ console.log("必须是数字") return false; } if(this.refs.cPage.value<1 || this.refs.cPage.value>this.props.allpage){ console.log("超过范围") return false; } this.props.GETLIST(this.refs.cPage.value); } componentDidMount() { console.log("我是分页组件"); } } export default DiReactPage
调用方式:
我们这个分页组件只是一个思想的梳理,是非常简单的,更复杂的我们可以加入更多的处理。