7.8 KiB
7.8 KiB
| name | description |
|---|---|
| jjb-loading-reset | 定义 JJB Loading 状态重置规范。在使用 resetModelState、弹窗/抽屉关闭或 confirmLoading 时使用。 |
接口与数据层规范
Loading 状态重置规范
⚠️ 重要约束:当
declareRequest定义的接口 Action 的 loading 状态(如loading、confirmLoading、submitLoading等)在可关闭组件或页面中使用时,必须在组件销毁或关闭时调用resetModelState重置对应的 loading 字段。
问题说明
declareRequest 定义的接口 Action 会自动管理 loading 状态(如 loading、confirmLoading、submitLoading 等)。但在以下场景中,loading 状态可能无法自动重置:
- 接口请求异常中断
- 弹窗/抽屉在请求过程中被关闭
- 组件卸载时请求仍在进行
- 页面跳转时请求仍在进行
这会导致 loading 状态一直保持为 true,下次打开组件或进入页面时仍然显示加载状态,影响用户体验。
适用场景
以下场景需要特别注意重置 loading 状态:
- 弹窗(Modal):弹窗关闭时
- 抽屉(Drawer):抽屉关闭时
- 页面组件:页面卸载时
- 条件渲染组件:组件被条件隐藏时
- 路由切换:路由跳转时
解决方案
在组件销毁或关闭时,调用 resetModelState 重置对应的 loading 字段。
使用示例
✅ 场景一:弹窗中使用 confirmLoading
import { useContext, useState, useEffect } from 'react';
import { Modal, Form, Input, Button } from 'antd';
import { Connect } from '@cqsjjb/jjb-dva-runtime';
import { InjectContext } from '~/enumerate/context';
import { NS_USER } from '~/enumerate/namespace';
function UserList({ createUserAction, resetModelState }) {
const context = useContext(InjectContext);
const [form] = Form.useForm();
const [modalOpen, setModalOpen] = useState(false);
// ✅ 推荐:使用 useEffect 监听弹窗状态,关闭时重置 loading
useEffect(() => {
if (!modalOpen) {
resetModelState(NS_USER, { confirmLoading: false });
}
}, [modalOpen]);
const handleSubmit = async (values) => {
const res = await createUserAction(values);
if (res.success) {
context.message.success('创建成功!');
setModalOpen(false);
}
};
return (
<>
<button onClick={() => setModalOpen(true)}>新增用户</button>
<Modal
open={modalOpen}
title="新增用户"
onCancel={() => setModalOpen(false)}
footer={[
<Button key="cancel" onClick={() => setModalOpen(false)}>
取消
</Button>,
<Button
key="submit"
type="primary"
loading={createUserAction?.confirmLoading}
onClick={() => form.submit()}
>
确定
</Button>
]}
>
<Form form={form} layout="vertical" onFinish={handleSubmit}>
<Form.Item name="name" label="用户名">
<Input placeholder="请输入用户名" allowClear />
</Form.Item>
</Form>
</Modal>
</>
);
}
export default Connect([NS_USER], true)(UserList);
✅ 场景二:抽屉中使用 submitLoading
import { useState, useEffect } from 'react';
import { Drawer, Form, Button } from 'antd';
import { Connect } from '@cqsjjb/jjb-dva-runtime';
import { NS_ORDER } from '~/enumerate/namespace';
function OrderDrawer({ updateOrderAction, resetModelState, drawerOpen, onClose }) {
const [form] = Form.useForm();
// ✅ 抽屉关闭时重置 loading 状态
useEffect(() => {
if (!drawerOpen) {
resetModelState(NS_ORDER, { submitLoading: false });
}
}, [drawerOpen]);
const handleSubmit = async (values) => {
const res = await updateOrderAction(values);
if (res.success) {
onClose();
}
};
return (
<Drawer
open={drawerOpen}
onClose={onClose}
footer={
<Button
type="primary"
loading={updateOrderAction?.submitLoading}
onClick={() => form.submit()}
>
提交
</Button>
}
>
<Form form={form} onFinish={handleSubmit}>
{/* 表单内容 */}
</Form>
</Drawer>
);
}
export default Connect([NS_ORDER], true)(OrderDrawer);
✅ 场景三:页面组件中使用 loading
import { useEffect } from 'react';
import { Connect } from '@cqsjjb/jjb-dva-runtime';
import { NS_PRODUCT } from '~/enumerate/namespace';
function ProductDetail({ product, fetchProductAction, resetModelState, productId }) {
useEffect(() => {
if (productId) {
fetchProductAction({ id: productId });
}
// ✅ 组件卸载时重置 loading 状态
return () => {
resetModelState(NS_PRODUCT, { loading: false });
};
}, [productId]);
if (product?.loading) {
return <div>加载中...</div>;
}
return <div>{product?.data?.name}</div>;
}
export default Connect([NS_PRODUCT], true)(ProductDetail);
✅ 场景四:条件渲染组件中使用 loading
import { useState, useEffect } from 'react';
import { Connect } from '@cqsjjb/jjb-dva-runtime';
import { NS_USER } from '~/enumerate/namespace';
function UserForm({ createUserAction, resetModelState, visible }) {
const [form] = Form.useForm();
// ✅ 组件隐藏时重置 loading 状态
useEffect(() => {
if (!visible) {
resetModelState(NS_USER, { confirmLoading: false });
}
}, [visible]);
if (!visible) {
return null;
}
return (
<Form form={form} onFinish={(values) => createUserAction(values)}>
{/* 表单内容 */}
<Button
type="primary"
htmlType="submit"
loading={createUserAction?.confirmLoading}
>
提交
</Button>
</Form>
);
}
export default Connect([NS_USER], true)(UserForm);
✅ 场景五:路由跳转时重置 loading
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
import { Connect } from '@cqsjjb/jjb-dva-runtime';
import { NS_ORDER } from '~/enumerate/namespace';
function OrderList({ orderList, fetchOrderListAction, resetModelState }) {
const location = useLocation();
useEffect(() => {
fetchOrderListAction();
}, []);
// ✅ 路由切换时重置 loading 状态
useEffect(() => {
return () => {
resetModelState(NS_ORDER, { loading: false, confirmLoading: false });
};
}, [location.pathname]);
return (
<div>
{/* 列表内容 */}
</div>
);
}
export default Connect([NS_ORDER], true)(OrderList);
核心原则
- 必须重置:当 loading 状态来源于
declareRequest时,组件销毁或关闭时必须重置 - 重置时机:
- 弹窗/抽屉:在
onCancel、afterClose或useEffect中监听关闭状态 - 页面组件:在
useEffect清理函数中重置 - 条件渲染:在
useEffect中监听显示状态 - 路由切换:在
useEffect清理函数中重置
- 弹窗/抽屉:在
- 重置字段:根据
declareRequest中定义的 loading 字段名进行重置(如loading、confirmLoading、submitLoading等) - 命名空间:使用正确的命名空间常量(如
NS_USER)调用resetModelState - 批量重置:可以一次性重置多个 loading 字段
批量重置示例
// ✅ 一次性重置多个 loading 字段
useEffect(() => {
if (!modalOpen) {
resetModelState(NS_USER, {
loading: false,
confirmLoading: false,
submitLoading: false
});
}
}, [modalOpen]);