# React 上拉加载和下拉刷新
# 工作遇到问题的解决方法
// 1,数据组装
Hlist: {
raw: [], // 一开始可以不声明, 方便判空
limit: 10,
pageNum: 1,
pageSize: 10,
totalCount: 0,
totalPage: 2,
}
// dispatch store里事件分发
*getHistoryOrderListV1({ params, callback }, { call, put, select }) {
const { Hlist } = yield select((state) => state.home);
const { page = 1, currency = "", size = 10, status = 0, isNew = false } =
params || {};
const { data } = yield call(
fetch.get,
api.getHistoryOrderList +
`?page=${page}&size=${size}¤cy=${currency}&status=${status}`
);
let dataCope =
page != 1
? [...(Hlist.rows || []), ...(data.data || [])]
: [...(data.data || [])];
if (callback) callback(true);
yield put({
type: "updateState",
state: {
Hlist: {
limit: 10,
pageNum: page,
pageSize: 10,
totalCount: data.count,
totalPage: Math.ceil(data.count / 10),
rows: dataCope,
},
},
});
},
// page里事件绑定
import React from "react";
import { connect } from "react-redux";
import { ListView, PullToRefresh, Icon, ActivityIndicator } from "antd-mobile";
import OrderItem from "@/components/orderItem/history";
import FooterLoading from "@/components/footerLoading";
import NoData from "@/components/noData";
import app from "@/app.config";
import "./index.scss";
@connect(
({ home: { Hlist }, loading: { effects } }) => {
return { Hlist, loading: effects["home/getHistoryOrderListV1"] };
},
dispatch => ({
getHistoryOrderList(params, callback) {
dispatch({ type: "home/getHistoryOrderListV1", params, callback });
},
homeUpdateState(state) {
dispatch({ type: "home/updateState", state });
}
})
)
class History extends React.Component {
constructor(props) {
super(props)
const ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2
});
this.state = {
dataSource: ds,
list: [],
currenct: '',
upLoading: false,
pullLoading: false
}
}
componentDidMount() {
const {
getHistoryOrderList,
Hlist: { rows }
} = this.props;
if (!rows) {
getHistoryOrderList({ page: 1 });
}
}
//上拉加载
onEndReached = (page, lastPage) => {
console.log({ page, lastPage })
//当前页小于总页数继续请求下一页数据,否则停止请求数据
const { getHistoryOrderList, currency } = this.props;
if (Number(page) < Number(lastPage)) {
// this.setState({ upLoading: true })
//接口请求下一页数据,完成后将upLoading设为false
getHistoryOrderList({ page: page + 1, currency }, (res) => {
if (res) {
this.setState({ upLoading: false })
}
})
}
}
//下拉刷新
onRefresh = (e) => {
this.setState({ pullLoading: true })
//接口请求第一页数据,完成后将pullLoading设为false
const { getHistoryOrderList, currency } = this.props;
getHistoryOrderList({ page: 1, currency }, (res) => {
if (res) {
this.setState({ pullLoading: false })
}
})
}
toDetail = (item) => {
const { homeUpdateState } = this.props;
homeUpdateState({
hisDetail: item
})
window.location.href = `#/${app.name}/historyDetail/${item.id}`;
};
//获取item进行展示
renderRow = (item, i) => {
return (
<div>
<OrderItem
toDetail={() => this.toDetail(item)}
item={item} />
</div>
)
}
render() {
const { dataSource, upLoading, pullLoading } = this.state;
const { Hlist, loading } = this.props;
if (loading && Hlist.pageNum == 1 && !Hlist.rows) {
return <div className="loading">
<ActivityIndicator text="正在加载" />
</div>
}
return (
<div className="goodsDetail">
{
Hlist && Hlist.rows && Hlist.rows.length ?
<ListView
dataSource={dataSource.cloneWithRows(Hlist.rows)}
renderRow={(rowData, id1, i) => this.renderRow(rowData, i)}
initialListSize={10}
pageSize={10}
renderFooter={() => (<div className="ListView-footer">
{(Hlist.pageNum < Hlist.totalPage) && !upLoading ? <p>下拉刷新</p> : upLoading ? <Icon type="loading" /> : <p>已加载完毕</p>}
</div>)}
onEndReached={() => this.onEndReached(Hlist.pageNum, Hlist.totalPage)}
onEndReachedThreshold={20}
useBodyScroll={true}
pullToRefresh={<PullToRefresh
refreshing={pullLoading}
onRefresh={(e) => this.onRefresh()}
/>}
/>
:
Hlist && Hlist.rows && !Hlist.rows.length ?
<NoData /> : null
}
</div>
)
}
}
export default History;
# 回到顶部
class Home extends Component {
consrcutor(props) {
super(props);
this.state = {
showScroll: false,
};
}
componentDidMount() {
window.addEventListener("scroll", this.props.toggleTopShow);
}
componentWillUnmount() {
window.removeEventListener("scroll", this.props.toggleTopShow);
}
toggleTopShow = () => {
let showScroll = "";
if (document.documentElement.scrollTop > 100) {
showScroll = true;
} else {
showScroll = false;
}
this.setState({
showScroll,
});
};
handleScrollTop = () => {
window.scrollTo(0, 0);
};
render() {
return (
<div>
{this.state.showScroll ? (
<BackTop onClick={this.handleScrollTop}>
<i className="iconfont ic-backtop"></i>
</BackTop>
) : null}
</div>
);
}
}
# 上拉加载下一页
页面结构为 banner + nav + content + footer,除 content 可以滚动,其他都固定,所有 scroll 绑定到 content 上
class Pool extends PureComponent {
construstor(props) {
super(props);
this.state = {
PullLoadingTip: "",
};
this.isLock = true;
this.PoolCon = React.createRef();
this.scrollToBottom = this.scrollToBottom.bind(this);
}
componentDidMount() {
this.ParentCon.current.addEventListener("scroll", this.scrollToBottom);
}
componentWillUnmount() {
this.ParentCon.current.removeEventListener("scroll", this.scrollToBottom);
}
scrollToBottom(event) {
event.stopPropagation();
const parent = event.target;
let scrolltop = parent.scrollTop; // content上部滚动出高度
let height = Zepto(parent).height(); // content的固定高度
let scrollheight = this.PoolCon.current.scrollheight; // content内部高度
if (scrollheight <= scrolltop + height) {
console.log("到低了");
if (Math.ceil(this.TOTAL / LIMIT) >= this.PAGE + 1) {
// 有下一页才 执行可以加载
this.setState(
{
isShowPullLoading: true,
},
() => {
if (this.isLock) {
// 请求下一页过程中不在发送请求
this.getData();
}
}
);
}
}
}
getData(flag) {
// 发送请求获取下一页数据,成功后isLock为true
this.isLock = false;
}
render() {
return (
<article onScroll={this.scrollToBottom} className="content">
<section ref={this.PoolCon}>
/** content列表内容 */
{this.state.isShowPullLoading ? (
<p className="pull-loading">正在加载...</p>
) : null}
</section>
</article>
);
}
}
# 移动端上实现上拉加载下一页
插件: iscroll, better-scroll 等 移动端一般监听的是 touch 事件,而不是 scroll
- touchstart: 当手触摸屏幕时触发,即使已经有一个手指放在屏幕上也会触发
- touchmove: 当手指在屏幕上滑动的时候连续触发,在这个事件发生期间调用 preventDefault()事件可以阻止滚动
- touchend: 当手指从屏幕上离开的时候触发
- touchcancel: 当系统停止跟踪触摸的时候触发 直接在 componentDidMount 中将 touch 相关的事件绑定到 content
// this.PoolCon = React.createRef();
// <article className="content" ref={this.ParentCon}>...</article>
componentMount(){
this.ParentCon.current.addEventListener("touchmove", ()=>this.scrollToBottom(event, 1),{passive: false});
this.ParentCon.current.addEventListener("touchend", ()=>this.scrollToButtom(event, 2));
}
componentWillUnmount(){
this.ParentCon.current.removeEventLister("touchmove", ()=>this.scrollToBottom(event, 1));
this.ParentCon.current.removeEventLister("touchend", ()=> this.scrollToBottom(event, 2));
}
scrollToBottom(event, type) {
event.stopPropagation();
const parent = this.ParentCon.current; //event.target;
let scrolltop = parent.scrollTop;
let height = Zepto(parent).height();
let scrollheight = this.PoolCon.current.scrollHeight;
if (scrollheight <= scrolltop + height) {
if (Math.ceil(this.TOTAL / LIMIT) >= this.PAGE + 1) {
if (1 === type) {
this.setState({
PullLoadingTip: "释放立即加载..."
});
} else if (2 === type) {
this.setState(
{
PullLoadingTip: "正在加载..."
},
() => {
if (this.isLock) {
this.getData();
}
}
);
}
}
}
}