254 lines
7.3 KiB
Markdown
254 lines
7.3 KiB
Markdown
|
|
---
|
|||
|
|
name: jjb-connect
|
|||
|
|
description: 定义 JJB 命名空间与视图连接规范。在使用 Connect 装饰器、defineNamespace、injectActionProps 或 resetModelState 时使用。
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 接口与数据层规范
|
|||
|
|
|
|||
|
|
## 命名空间与视图连接
|
|||
|
|
|
|||
|
|
`Connect` 是 `@cqsjjb/jjb-dva-runtime` 提供的高阶组件(HOC),用于将 Dva 模型(Model)与 React 组件连接。它支持自动注入模型 state、工具方法,以及(可选的)请求 Action,简化了组件与数据层的交互。
|
|||
|
|
|
|||
|
|
## 命名空间定义规范
|
|||
|
|
|
|||
|
|
- **集中定义**:命名空间必须集中在 `src/enumerate/namespace/index.js` 中定义
|
|||
|
|
|
|||
|
|
- **定义方式**:使用 `defineNamespace` 函数定义命名空间常量
|
|||
|
|
|
|||
|
|
- **命名规则**:命名空间常量对应 `src/api/<namespace>/index.js` 文件
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
// src/enumerate/namespace/index.js
|
|||
|
|
import { defineNamespace } from '@cqsjjb/jjb-dva-runtime';
|
|||
|
|
|
|||
|
|
// 这里 user 指的是 src/api/user/index.js
|
|||
|
|
export const NS_USER = defineNamespace('user');
|
|||
|
|
|
|||
|
|
// 这里 order 指的是 src/api/order/index.js
|
|||
|
|
export const NS_ORDER = defineNamespace('order');
|
|||
|
|
|
|||
|
|
// 这里 examList 指的是 src/api/examList/index.js
|
|||
|
|
export const NS_EXAM_LIST = defineNamespace('examList');
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 请求声明规范
|
|||
|
|
|
|||
|
|
- **文件位置**:每个命名空间常量对应一个 `src/api/<namespace>/index.js` 文件
|
|||
|
|
|
|||
|
|
- **声明方式**:使用 `declareRequest` 声明接口请求(具体用法参考 [`02-接口定义规范`](../02-接口定义规范/SKILL.md))
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
// src/api/user/index.js
|
|||
|
|
import { declareRequest } from '@cqsjjb/jjb-dva-runtime';
|
|||
|
|
|
|||
|
|
// 获取用户信息
|
|||
|
|
export const fetchUserAction = declareRequest(
|
|||
|
|
'loading',
|
|||
|
|
'Get > /api/user/{id}',
|
|||
|
|
'userInfo: {} | res.data || {}'
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// 新增用户(使用 @ 标记)
|
|||
|
|
export const addUserAction = declareRequest(
|
|||
|
|
'confirmLoading',
|
|||
|
|
'Post > @/api/user'
|
|||
|
|
);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Connect API 说明
|
|||
|
|
|
|||
|
|
- **函数签名**:`Connect(models, injectActionProps?)`
|
|||
|
|
|
|||
|
|
- **参数说明**:
|
|||
|
|
|
|||
|
|
| 参数 | 类型 | 必填 | 说明 |
|
|||
|
|
| :--- | :--- | :--- | :--- |
|
|||
|
|
| models | Namespace[] | 是 | 命名空间数组,必须从 `src/enumerate/namespace` 引入 |
|
|||
|
|
| injectActionProps | boolean | 否 | 是否自动注入 action 方法,默认为 `false` |
|
|||
|
|
|
|||
|
|
- **injectActionProps 参数说明**:
|
|||
|
|
|
|||
|
|
- `false`(默认):只能使用 `dispatchModelAction(namespace, effectAction, payload)` 手动调用
|
|||
|
|
|
|||
|
|
- `true`:所有 `declareRequest` 定义的请求会自动注入到组件 props 中,可直接调用
|
|||
|
|
|
|||
|
|
## 注入的 Props
|
|||
|
|
|
|||
|
|
1. **State 注入**:
|
|||
|
|
|
|||
|
|
- `props.user` → 对应 `NS_USER` 模型
|
|||
|
|
|
|||
|
|
- `props.order` → 对应 `NS_ORDER` 模型
|
|||
|
|
|
|||
|
|
- 每个命名空间对应一个 props 属性,包含该模型的所有 state(数据字段和 Loading 状态)
|
|||
|
|
|
|||
|
|
2. **工具方法**(始终存在):
|
|||
|
|
|
|||
|
|
- `models`:所有连接的模型
|
|||
|
|
|
|||
|
|
- `getModelState(namespace, name)`:获取模型状态
|
|||
|
|
|
|||
|
|
- `resetModelState(namespace, state)`:重置模型状态
|
|||
|
|
|
|||
|
|
- `dispatchModelAction(namespace, effectAction, payload?)`:手动调用 Action
|
|||
|
|
|
|||
|
|
- `dispatch(type, payload?)`:Dva dispatch 方法
|
|||
|
|
|
|||
|
|
3. **Action 方法**(当 `injectActionProps = true` 时才存在):
|
|||
|
|
|
|||
|
|
- 例如 `declareRequest` 定义的 `fetchUserAction` 会在组件 props 中直接可用
|
|||
|
|
- Action 方法返回 Promise,使用时需要通过 `success` 字段判断接口是否成功:
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
this.props.xxxAction().then(res => {
|
|||
|
|
// 必须通过 success 判断这个接口是否成功
|
|||
|
|
if (res.success) {
|
|||
|
|
// 接口成功后的处理逻辑
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- **注意**:如果接口失败了,不需要手动抛出接口异常错误,http 内部模块已自动拦截并提示错误
|
|||
|
|
|
|||
|
|
## 使用方式
|
|||
|
|
|
|||
|
|
### 方式一:默认模式(injectActionProps = false) - 不推荐
|
|||
|
|
|
|||
|
|
> **说明**:以下示例仅用于对比理解两种方式的区别,实际开发中请使用推荐的方式二。
|
|||
|
|
|
|||
|
|
此时不会自动注入 Action,需要手动调用 `dispatchModelAction`:
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
import React, { useEffect } from 'react';
|
|||
|
|
import { Connect } from '@cqsjjb/jjb-dva-runtime';
|
|||
|
|
import { NS_USER } from '~/enumerate/namespace';
|
|||
|
|
|
|||
|
|
function UserProfile({ user, dispatchModelAction }) {
|
|||
|
|
useEffect(() => {
|
|||
|
|
dispatchModelAction(NS_USER, 'fetchUserAction', { id: 1 });
|
|||
|
|
}, []);
|
|||
|
|
|
|||
|
|
console.log(user?.loading);
|
|||
|
|
console.log(user?.confirmLoading);
|
|||
|
|
|
|||
|
|
return <div>{user.userInfo?.name}</div>;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 注意这里第二个参数没有传(默认为 false)
|
|||
|
|
export default Connect([NS_USER])(UserProfile);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 方式二:自动注入 Action(injectActionProps = true) - 推荐
|
|||
|
|
|
|||
|
|
当 `injectActionProps = true` 时,模型中通过 `declareRequest` 声明的请求会自动注入到组件 props 中,使用时无需 `dispatchModelAction`,可以直接调用:
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
import React, { useEffect } from 'react';
|
|||
|
|
import { Connect } from '@cqsjjb/jjb-dva-runtime';
|
|||
|
|
import { NS_USER } from '~/enumerate/namespace';
|
|||
|
|
|
|||
|
|
function UserProfile({ user, fetchUserAction }) {
|
|||
|
|
useEffect(() => {
|
|||
|
|
fetchUserAction({ id: 1 });
|
|||
|
|
}, []);
|
|||
|
|
|
|||
|
|
console.log(user?.loading);
|
|||
|
|
console.log(user?.confirmLoading);
|
|||
|
|
|
|||
|
|
return <div>{user.userInfo?.name}</div>;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 第二个参数传 true,启用 Action 自动注入到 props
|
|||
|
|
export default Connect([NS_USER], true)(UserProfile);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 类组件装饰器语法
|
|||
|
|
|
|||
|
|
如果要连接 class 类组件,使用装饰器语法:
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
import React, { Component } from 'react';
|
|||
|
|
import { Connect } from '@cqsjjb/jjb-dva-runtime';
|
|||
|
|
import { NS_USER } from '~/enumerate/namespace';
|
|||
|
|
|
|||
|
|
@Connect([NS_USER], true)
|
|||
|
|
class UserProfile extends Component {
|
|||
|
|
componentDidMount() {
|
|||
|
|
this.props.fetchUserAction({ id: 1 });
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
render() {
|
|||
|
|
const { user } = this.props;
|
|||
|
|
return <div>{user.userInfo?.name}</div>;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export default UserProfile;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 多个模型连接示例
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
import React, { useEffect } from 'react';
|
|||
|
|
import { Connect } from '@cqsjjb/jjb-dva-runtime';
|
|||
|
|
import { NS_USER, NS_ORDER } from '~/enumerate/namespace';
|
|||
|
|
|
|||
|
|
function Dashboard({ user, order, fetchUserAction, fetchOrdersAction }) {
|
|||
|
|
useEffect(() => {
|
|||
|
|
fetchUserAction({ id: 1 });
|
|||
|
|
fetchOrdersAction();
|
|||
|
|
}, []);
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div>
|
|||
|
|
<h3>{user?.userInfo?.name}</h3>
|
|||
|
|
<ul>
|
|||
|
|
{order.list?.map(o => (
|
|||
|
|
<li key={o.id}>{o.title}</li>
|
|||
|
|
))}
|
|||
|
|
</ul>
|
|||
|
|
</div>
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export default Connect([NS_USER, NS_ORDER], true)(Dashboard);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 重置状态示例
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
import { useEffect } from 'react';
|
|||
|
|
import { Connect } from '@cqsjjb/jjb-dva-runtime';
|
|||
|
|
import { NS_USER } from '~/enumerate/namespace';
|
|||
|
|
|
|||
|
|
function UserProfile({ user, fetchUserAction, resetModelState }) {
|
|||
|
|
useEffect(() => {
|
|||
|
|
fetchUserAction({ id: 1 });
|
|||
|
|
|
|||
|
|
return () => {
|
|||
|
|
// 组件销毁时重置状态
|
|||
|
|
resetModelState(NS_USER, {
|
|||
|
|
loading: false,
|
|||
|
|
userInfo: {}
|
|||
|
|
});
|
|||
|
|
};
|
|||
|
|
}, []);
|
|||
|
|
|
|||
|
|
return <div>{user.userInfo?.name}</div>;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export default Connect([NS_USER], true)(UserProfile);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 使用规范总结
|
|||
|
|
|
|||
|
|
1. 命名空间必须集中在 `src/enumerate/namespace/index.js` 中定义,使用 `defineNamespace` 函数
|
|||
|
|
|
|||
|
|
2. 请求声明必须放在 `src/api/<namespace>/index.js` 中,使用 `declareRequest`
|
|||
|
|
|
|||
|
|
3. `Connect` 负责把 state、action 方法注入到组件
|
|||
|
|
|
|||
|
|
4. 如果 `injectActionProps = true`,state、action 自动注入到组件 props,免去手动处理
|
|||
|
|
|
|||
|
|
5. **禁止行为**:禁止在页面内使用 `useState` 管理本应由 `declareRequest` 处理的接口数据或 Loading 状态
|