实现“加载更多”的换页功能
实现功能: 在List.js中增加“加载更多”按钮,点击按钮后换页。
实现 点击 加载更多:
- 模拟接口数据(public-api-homeList.json)
- 定义组件(style.js)
- 点击后要发送AJAX请求,AJAX请求是写在action中的(actionCreators.js)
- 使用组件,绑定点击事件,在事件函数中调用方法去创建并派发action(相当于发送AJAX请求)(List.js)
- store拿到action后打包action和state传给reducer
- reducer拿到action和state后去改变articleList后返回新state(reducer.js)
- 页面重新渲染
实现 换页:
- 在reducer中新增 articlePage 用于存储当前页数,初始值为1(reducer.js)
- 在List.js中每点击一次按钮就使articlePage+1,所以要在调用的函数中传入page
- 要使 articlePage+1 改变就要通过action,所以在actionCreators.js中进行设置。
- 使AJAX请求的接口地址上使用articlePage,这也就保证了每次请求的地址都是带有不同页数的。
- 要让articlePage+1就要通过action改变store中数据
- reducer其实还是返回homeList.json中的模拟数据,只是方便了后端。
实现 加载更多数据
定义 组件LoadMore
在style.js中**定义 组件LoadMore
**:
1 | export const LoadMore = styled.div` |
模拟接口数据
在public-api下新建homeList.json文件用于模拟接口数据:
1 | { |
使用组件,绑定点击事件
List.js:
1 | import React, { Component, Fragment } from 'react'; |
- 在List.js中引入并使用 组件
LoadMore
,绑定点击事件,点击以后我们是要发送AJAX请求获取数据的,获取到的数据改变state中数据让页面重新渲染- AJAX请求要放到action中,在actionCreators.js中新建**getMoreList()用于创建 发送AJAX请求的action,新建addHomeList()**用于创建 改变store中数据的action。
- 要派发action给store就要在connect方法的参数2(回调函数)中**新建函数getMoreList()**用于派发action
- 事件绑定函数中只需**调用函数getMoreList()**即可完成AJAX的发送
创建2个action
actionCreators.js中添加以下代码,创建发送AJAX请求和改变state数据的action:
1 | import { fromJS } from "immutable"; |
1 | const addHomeList = (list) => ({ |
- 新建**getMoreList()**用于创建 发送AJAX请求的action
- 新建**addHomeList()**用于创建 改变store中数据的action。
- 用于改变store中的数据的数据也必须是immutable类型的。
- immutable提供的fromJS()和List()都可以使普通数组转换为immutable数组。
- 但是List只能使数组变immutable,数组元素若为对象,则还是普通对象。fromJS()是将数组元素也immutable化的。
reducer处理articleList
reducer接收到action后,使用concat()将 articleList变成合并了新数据后的数组,返回给state:
1 | case constants.ADD_HOME_List: |
实现 换页
reducer新增articlePage管理当前页码
- 在reducer中给state新增一个默认数据
articlePage:1
,默认起始页为1,接下来每点击一次“加载更多”就使articlePage+11
2
3
4
5
6const defaultState = fromJS({
topicList: [],
articleList: [],
recommendList: [],
articlePage:1,
});
List中获取articlePage传给action使用
在List.js中获取articlePage:
1 | const mapState = (state) => ({ |
调用点击事件绑定函数getMoreList()时传入articlePage:
1 | const { list, page, getMoreList } = this.props; |
1 | <LoadMore onClick={() => getMoreList(page)}>加载更多</LoadMore> |
getMoreList()拿到page后传给 actionCreators.js中的getMoreList():
1 | const mapDispatch = (dispatch) => ({ |
actionCreators中page+1
actionCreators.js中,在**请求后端接口数据时加上page("/api/homeList.json?page=" + page
**)
1 | export const getMoreList = (page) => { |
可以看到每次点击“加载更多”请求的都是第一页:
把page也通过addHomeList
reducer处理articlePage
action传递了nextPage,reducer就将接收到的nextPage放到state中替换掉articlePage,则每一次点击后请求的接口都带有不同的页数:
1 | case constants.ADD_HOME_List: |
这样一来每次点击请求的页码都不同:
注意
后端拿到不同的页码就可以返回不同的数据了。(但实际上我们每次返回的还是homeList.json中的模拟数据)
实现 返回顶部 功能
- 在home-index.js中我们实现一个点击以后返回顶部的按钮。
style.js中定义组件BackTop
由于是一直在右下角的,所以该按钮是固定定位的:
1 | export const BackTop = styled.div` |
home-index.js中使用BackTop
BackTop绑定点击事件
要想让按钮点击回到顶部,就需要给他绑定点击事件:
1 | <BackTop onClick={this.handleScrollTop}>返回顶部</BackTop> |
由于此时和reducer并不关系,只是单纯回到顶部,所以只需要在Home组件中书写函数即可:
1 | handleScrollTop() { |
我们需要借助window.scrollTo()
方法把内容滚动到指定的坐标。scrollTo(x 坐标,y 坐标)
下滑到一定位置再显示该按钮
实现功能:此时按钮一直显示在页面上,我们要实现下滑到一定位置再显示该按钮。
- 在reducer中添加
showScroll:false
默认不显示按钮: - 到home-index.js中拿到数据showScroll:
- 使用三元运算符来根据showScroll决定是否显示按钮:
- 使用addEventListener()监听onScroll事件,元素滚动条在滚动时就触发 changeScrollTopShow():
- 在 changeScrollTopShow()中根据
document.documentElement.scrollTop
得到滚动距离:由于我们需要通过改变store中数据来决定是否显示按钮,所以函数changeScrollTopShow要放在connect的参数2中。 - 在changeScrollTopShow()中设置判断,滚动距离顶部距离大于400才显示,否则隐藏按钮,要修改store中数据就要派发action,所以需要在 actionCreators.js中创建对应action:
- 在reducer中处理action:
- 注意:在home组件中给window绑定了一个事件,那么移除home组件时要记得解绑:
优化代码
将修改state中数据的代码拿出来放在函数里,在switch…case中通过调用函数对state进行修改,精简switch…case的代码:
首页性能优化(PureComponent)
- 我们在首页所有组件都使用connect与store做了链接,这也就导致只要store发生改变,那么每个组件都会被重新渲染,也就是render函数会被重新执行,也就是说不管更新的数据和本组件是否有关系,本组件都会重新渲染,这样就导致性能不高。
解决方法:
- 所以可以使用 shouldCompUpdate()优化代码,判断只有和本组件有关的数据更新时,本组件的render函数才执行,否则return false不让组件重新渲染。通过避免虚拟DOM的比对来提高性能。
- 但是手写shouldCompUpdate太过麻烦,所以我们可以使用react提供的PureComponent
- 前提条件:必须使用immutable管理数据才能使用PureComponent,否则坑很多。
- 在首页index.js及其内部组件中都使用PureComponent代替Component:
路由跳转(react-router-dom的Link组件)
- 使用react-router-dom的Link组件帮助进行页面跳转,Link组件类似a标签,to属性类似a标签的href属性。
- 使用Link组件可使页面跳转时不发送另一个http请求,提升加载速度,提高性能。
- 注意:react是 单页应用的跳转,也就是说,不管怎么进行页面跳转,整个网站只会加载一次html文件,这也就决定了不能使用
<a>
标签进行页面跳转。- 如果使用
<a>
标签进行页面跳转,跳转时会发送HTTP请求。 - 而借助react-router-dom的Link组件实现跳转则不会,因此加载速度会快很多,借此也可提高性能。
- 如果使用
首页到详情页
希望点击博客时可跳转到详情页:
在List.js中引入react-router-dom的Link组件:
1 | import { Link } from "react-router-dom"; |
可以看到跳转时并没有发送另一个HTTP请求:
详情页到首页
实现功能:点击Logo从详情页跳转到首页。
在common-header-styles.js中,原本我们的Logo组件使用的是a标签,现在改成div:
1 | export const Logo = styled.div` |
在common-header-index.js中引入并使用Link组件包裹Logo组件,使之可跳转首页:
1 | import { Link } from "react-router-dom"; |
1 | <Link to="/"> |
此时会报错:Error: Invariant failed: You should not use <Link> outside a <Router>
也就是说Link不应该在router的外部:
(关于Router组件可复习这篇笔记)
到App.js中可以看到,Header和BrowserRouter组件是并列关系,也就是说,处于Header组件中的Link组件和BrowserRouter组件是并列关系,Link也就在router的外部了:
解决方法:将Header组件放入BrowserRouter组件内部: