表格字段明细展示,属于比较小的需求,但是也有一定交互细节,本文选取部分场景。
1、字段渲染
- render和渲染组件是有区别的。
- render常见为函数转化,利用页面和全局的变量state来渲染,最终返回Dom结构,偏向于展示和数据处理。函数参数和原来一致。
- 渲染组件提供了自己的状态,有利于该字段的交互逻辑集中在组件中,尽量少利用外部变量。注意函数参数的变化,由props引入。
- 命名方式略有不同,函数驼峰即可,组件大写字母开头+驼峰。
import { Table } from "antd";
import React, { useState } from "react";
const RenderNameCom = ({ text }) => {
const [loading, setLoading] = useState(false);
return (
<div
onClick={() => {
setLoading(true);
setTimeout(() => {
setLoading(false);
}, 2000);
}}
>
{loading ? "Loading..." : text}
</div>
);
};
const renderNameFun = (text) => {
const textLast = text > 10 ? "..." : text;
return <div>{textLast}</div>;
};
export const columns = [
{
title: "Name",
dataIndex: "name",
render: (text) => <RenderNameCom text={text} />,
},
{
title: "Age",
dataIndex: "age",
render: renderNameFun,
},
];
const testPage = () => {
return (
<Table columns={columns} dataSource={[{ name: "John Doe", age: 32 }]} />
);
};
export default testPage;
2、异步请求展示明细
- click点击比较常见,单次点击也比较保守和稳定,配合disabled(或者loading)保证同一时间不在接受触发事件。
- 获取展开状态visible(新版本使用open属性),可以进行灵活判断,不再进行触发请求。
- 如果没有disabled禁用,连续点击触发多次,弹框
import { Table } from "antd";
import React, { useState } from "react";
import { Button, Popover, Spin, Divider } from "antd";
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const RenderNameCom = ({ text }) => {
const [show, setShow] = useState(false);
const [loading, setLoading] = useState(false);
const [data, setData] = useState({
width: 0,
height: 0,
top: 0,
left: 0,
});
const onClick = async () => {
// 当未展开时,点击可以发起请求;展开后,默认会关闭,阻止多余请求
if (show) {
return;
}
try {
setLoading(true);
await delay(2000);
setData({
width: 100,
height: 100,
top: 100,
left: 100,
});
console.log("clicked");
setLoading(false);
} catch (error) {
console.log(error);
setLoading(false);
}
};
return (
<Popover
content={
loading ? (
<Spin />
) : (
<div>
<p>Name: {text}</p>
<Divider />
<p>Width: {data?.width}</p>
<p>Height: {data?.height}</p>
<p>Top: {data?.top}</p>
<p>Left: {data?.left}</p>
</div>
)
}
trigger="click"
visible={show}
onVisibleChange={(visible) => setShow(visible)}
>
<Button type="link" onClick={onClick} disabled={loading}>
{text}
</Button>
</Popover>
);
};
export const columns = [
{
title: "Name",
dataIndex: "name",
render: (text) => <RenderNameCom text={text} />,
},
];
const testPage = () => {
return (
<Table columns={columns} dataSource={[{ name: "John Doe", age: 32 }]} />
);
};
export default testPage;
3、hover展示问题
3.1 基本逻辑
- 用click的基础代码,改为hover触发基本完成任务
- 问题在于hover存在鼠标滑过频率很快的问题,误触发概率很大。
import { Table } from "antd";
import React, { useState } from "react";
import { Button, Popover, Spin, Divider } from "antd";
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const RenderNameCom = ({ text }) => {
const [show, setShow] = useState(false);
const [loading, setLoading] = useState(false);
const [data, setData] = useState({
width: 0,
height: 0,
top: 0,
left: 0,
});
const onClick = async () => {
console.log("clicked start");
if (show) {
return;
}
try {
setLoading(true);
await delay(2000);
setData({
width: 100,
height: 100,
top: 100,
left: 100,
});
console.log("clicked");
setLoading(false);
} catch (error) {
console.log(error);
setLoading(false);
}
};
return (
<Popover
content={
loading ? (
<Spin />
) : (
<div>
<p>Name: {text}</p>
<Divider />
<p>Width: {data?.width}</p>
<p>Height: {data?.height}</p>
<p>Top: {data?.top}</p>
<p>Left: {data?.left}</p>
</div>
)
}
trigger="hover"
visible={show}
onVisibleChange={(visible) => setShow(visible)}
>
<Button type="link" onMouseEnter={onClick} disabled={loading}>
{text}
</Button>
</Popover>
);
};
export const columns = [
{
title: "Name",
dataIndex: "name",
render: (text) => <RenderNameCom text={text} />,
},
];
const testPage = () => {
return (
<Table
style={{ paddingTop: "100px" }}
columns={columns}
dataSource={[{ name: "John Doe", age: 32 }]}
/>
);
};
export default testPage;
3.2 hover时长判断
判断鼠标hover时长,决定是否触发事件;基础代码模拟。
const HoverTimer = () => {
const [loading, setLoading] = useState(false);
const timer = useRef(null);
const onMouseEnter = async () => {
timer.current = setTimeout(async () => {
try {
setLoading(true);
await delay(2000);
console.log("clicked");
setLoading(false);
} catch (error) {
console.log(error);
setLoading(false);
}
}, 1000);
};
const onMouseLeave = () => {
if (timer.current) {
clearTimeout(timer.current);
}
};
return (
<Button
type="link"
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
loading={loading}
>
Hover me
</Button>
);
};
3.3 render+hover
- hover会自动展开和关闭,可以不再设置show的状态。
- 注意hover刚开始,定时器未执行,利用defaultData的初始状态进行设置loading
import { Table } from "antd";
import React, { useState, useRef } from "react";
import { Button, Popover, Spin, Divider } from "antd";
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const RenderNameCom = ({ text }) => {
const [loading, setLoading] = useState(false);
const defaultData = {
width: 0,
height: 0,
top: 0,
left: 0,
};
const [data, setData] = useState(defaultData);
const timer = useRef(null);
const onMouseEnter = async () => {
console.log("clicked start");
setData(defaultData); // 同步清空
timer.current = setTimeout(async () => {
try {
setLoading(true);
await delay(2000);
setData({
width: 100,
height: 100,
top: 100,
left: 100,
});
console.log("clicked");
setLoading(false);
} catch (error) {
console.log(error);
setLoading(false);
}
}, 1000);
};
const onMouseLeave = () => {
if (timer.current) {
clearTimeout(timer.current);
}
};
return (
<Popover
content={
loading || data?.width === 0 ? (
<Spin />
) : (
<div>
<p>Name: {text}</p>
<Divider />
<p>Width: {data?.width}</p>
<p>Height: {data?.height}</p>
<p>Top: {data?.top}</p>
<p>Left: {data?.left}</p>
</div>
)
}
trigger="hover"
>
<Button
type="link"
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
disabled={loading}
>
{text}
</Button>
</Popover>
);
};
export const columns = [
{
title: "Name",
dataIndex: "name",
render: (text) => <RenderNameCom text={text} />,
},
];
const testPage = () => {
return (
<div>
<Table
style={{ paddingTop: "100px" }}
columns={columns}
dataSource={[{ name: "John Doe", age: 32 }]}
/>
</div>
);
};
export default testPage;