背景
公司都有运营拉流量的需求,一般落地活动页也就是单页面应用,不会涉及到很复杂逻辑数据交互,但是别看是一个小小的活动页,作者刚毕业,也是被狠狠拷打了一番,下面就作者实践思考之后,沉淀出一些在开发遇到的坑,和通用的设计方案,减少被PM和QA狠狠diss。
需求设计
对于活动页面,常常需要几个变量来控制状态,如:loading(加载数据), pageError(出现未知错误), getData(获取数据),以及empty(空数据)状态。
流程设计:
流程说明: 用户进入到我们的页面,初始状态应该是一个loading状态,并且在React or Vue生命周期的时候去请求接口数据,在数据返回回来时根据数据状态,来判断页面该显示什么样子。
以下是对通用的hook进行说明
import React, { useState } from 'react';
export interface Data {
items: number[];
total: number;
page: number;
pageSize: number;
}
interface Props {
fetch: (params: any) => Promise<Data>;
}
export const useHooks = ({ fetch }: Props) => {
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<boolean>(false);
const [dataObject, setDataObject] = useState<Data>({} as Data);
const getData = async (params: any) => {
setLoading(true);
// 必须try catch 万一 QA给你来个断网 不炸锅了吗?
try {
const res = await fetch(params);
const { items, total, page, pageSize } = res;
setDataObject({
items: dataObject.items.concat(items),
total,
page,
pageSize
});
} catch {
setError(true);
} finally {
setLoading(false);
}
};
const isNeedFetch = () => {
if (loading) {
return false;
}
let needFetch = false;
const scrollItme = document.getElementById('scroll') || {
scrollTop: 0,
clientHeight: 0,
scrollHeight: 0
};
if (scrollItme.scrollTop + scrollItme.clientHeight > scrollItme.scrollHeight - 150) {
needFetch = true;
}
return needFetch;
};
// 上拉加载
const nextFetch = async (params: any) => {
if (!isNeedFetch()) {
return;
}
await getData(params);
};
return {
loading,
error,
getData,
nextFetch,
dataObject
};
};
import React, { useEffect } from 'react';
import { Data, useHooks } from './useHooks';
import { Empty } from 'antd';
const api = (params: any) =>
new Promise((resolve, reject) => {
resolve({
items: [],
total: 100,
page: 1,
pageSize: 10
});
}) as Promise<Data>;
export const Landing = () => {
const { loading, error, getData, nextFetch, dataObject } = useHooks({ fetch: api });
// 下拉刷新
const handleScroll = (e: any) => {
const scrollElement = e.currentTarget;
if (scrollElement.scrollTop <= 10 && !loading) {
return;
}
nextFetch({});
};
useEffect(() => {
getData({
page: 1,
pageSize: 10
});
}, []);
return (
// 这里可以二次封装,让页面只关心list 对error,empty loading封装到组件内部
<div>
{error && <Error />}
{dataObject && dataObject.items.length === 0 ? (
<Empty />
) : (
<div id="scroll" onScroll={handleScroll}>
{dataObject?.items.map((item, index) => (
<div key={index}>{item}</div>
))}
</div>
)}
{loading && <Loading />}
</div>
);
};
总结
业务上 也主要实现了Error loading 以及实现下拉加载,当然了,在这个过程中,如果在Error组件出现的时候,也应该实现重新加载数据,这种情况可以再考虑吧useHooks封装到一个context里面,这样组件共享状态,使得出了错误也能重试。当然 如果有多个类似相同元素,比如直播活动等等,可以考虑封装一个公用的组件负责loading Error 下拉加载,这样开发就只需要关注获取list,无需关注Error,loading等,从而提高开发效率。