1. 自定义封装组件
1.1 组件通信
1.1.1 父传子
在本项目中对因为删除组件比较通用,所以对删除组件进行了封装。
如下图我们对定义了通用删除组件,通过props传入回调方法,这样页面上只需要引用该组件,并传入自定义的删除函数即可引入。
我们可以使用props来进行组件间的通信,但是props入参是只读的,所以无法对其进行修改,所以需要传入回调函数
我们可以在父组件中传入函数并给入父组件的对象入参,在子组件中通过props传入的function函数来进行调用
父传子样例:
子组件
type IsvDeleteProps = {
deleteCallBack: Function;
};
const IsvDelete: React.FC<IsvDeleteProps> = (props: any) => {
function showDeleteConfirm() {
confirm({
title: '请问是否需要删除?',
icon: <ExclamationCircleOutlined />,
content: '',
okText: '是',
okType: 'danger',
cancelText: '否',
onOk() {
props.deleteCallBack();
},
onCancel() {},
});
}
return (
<>
<a
onClick={() => {
showDeleteConfirm();
}}
>
删除
</a>
</>
);
};
export default IsvDelete;
父组件:
function removeApp(appCode: string) {
removeRule({ appCode }).then((resp) => {
if (resp.success) {
message.success('删除成功');
}
formRef.current.submit();
});
}
<IsvDelete
deleteCallBack={() => {
removeApp(record.appCode);
}}
/>,
1.1.2 子传父
我们也可以通过props传入回调函数,接收子组件传入的对象,来控制子组件的(例如:formRef,可以通过父组件控制子组件)
如样例:自组件在初始化时会执行props.formRef,此时将定义好的form传入回调函数中,即可将子组件表单的form对象作为入参传入回调函数,父组件中定义的回调函数接受接收到子组件入参后将对象保存在父组件中,即可通过该对象控制子组件的form表单
子传父样例:
父组件
//回调赋值
const baseCallback = (formRef) => {
basicFormRef = formRef;
};
<BasicInformationView
fromRef={baseCallback}
/>
子组件:
const [form] = Form.useForm();
if ('fromRef' in props) {
props.fromRef(form);
}
2. 函数组件hook钩子
介绍: https://juejin.cn/post/6844904036433395720 基用React Hooks + Antd快速实现一个列表页
hook支持函数组件模拟类式组件生命周期
模拟组件初次挂载成功后钩子componentDidMount
如下,第二个参数为[]数组,那么在页面初次挂载时会触发一次,执行回调函数
useEffect(() => {
xxxxx
}, []);
模拟组件更新后挂载钩子componentDidUpdate
如下,第二个参数为监听的对象,那么在页面监听对象发生变化时,执行回调函数
statsAA,props.xx发生变化时会
useEffect(() => {
xxxxx
}, [statsAA,props.xx]);
模拟组件解除挂载时的钩子componentWillUnMount
如下,在箭头函数中添加返回值,那么在页面解除绑定时会调用return的钩子函数
useEffect(() => {
return () => {
xxx
};
}, []);
3.定时器相关问题
useState与useRef的使用场景区别:
(1) useState的值在每个rernder中都是独立存在的。而useRef.current则更像是相对于render函数的一个全局变量,每次他会保持render的最新状态。
useState值的更新会触发组件重新渲染,而useRef的current不会出发重渲染。
(2) 在使用定时器时,定时器需要使用useRef进行存储,如果定时器使用useStatus存储会出现在接触挂载时,获取state的值为undefined 导致定时器无法清除。
const autoLoad = useRef();
useEffect(() => {
getLog();
autoLoad.current = setInterval(() => {
getLog();
}, 5000);
return () => {
clearInterval(autoLoad.current);
};
}, []);
function onChange(checked) {
clearInterval(autoLoad.current);
if (checked == true) {
autoLoad.current = setInterval(() => {
getLog();
}, 5000);
}
}
4.form表单相关问题
4.1 表单项给初始值但提交表单时该值未提交
表单值初始化需要在Form.Item节点上通过initialValue进行初始化,而不是通过输入框控件的defaultValue属性进行初始化值
<Form.Item
label="代码类型"
name="codeType"
rules={[{ required: true, message: '请选择代码类型' }]}
initialValue={props.basicCodeType}
>
<Select
placeholder="请选择代码类型!"
// disabled={isBan()}
disabled={true}
onChange={handleChangeCodeType}
>
{optionCodeTypes.map((itm) => (
<option key={itm}>{itm}</option>
))}
</Select>
</Form.Item>
4.2 表单复选框反显问题
表单中复选框在返显时,如果未添加valuePropName="checked"属性,导致复选框一直使用initialValue默认值,而不是使用表单的name传过来的值
<Form.Item
name="enableLogCollect"
valuePropName="checked"
rules={[{ required: false }]}
noStyle={true}
initialValue={true}
>
<Checkbox disabled={isHide()}>系统自动配置</Checkbox>
</Form.Item>
4.3表单输入框联动方案
通过表单api : onValuesChange ,在表单值变化时都会调用起该函数,通过该函数监听指定name输入框的值,可以实现两个输入框值同步修改的问题
const formChange = (changedValues) => {
if (changedValues.appName != undefined) {
form.setFieldsValue({ gitAppName: changedValues.appName });
}
};
<Form form={form} {...formItemLayout} onValuesChange={formChange}>
<Form.Item
name="gitAppName"
rules={[{ required: true, message: '请输入代码仓库' }]}
noStyle
>
<Input style={{ width: '35%' }} disabled={true} />
</Form.Item>
<Form.Item
label="应用名称"
name="appName"
rules={[
{ required: true, message: '请输入应用名称含英文字母,数值,下划线,中横线等字符!' },
]}
>
<Input
placeholder="applictaion1,含英文字母,数值,下划线,中横线等字符!"
disabled={isBan() || props.optionType == 'edit'}
/>
</Form.Item>
</Form>
5. 表格相关问题
5.1 表格刷新由其他表单组件控制
修改其他表单项数据时,自动刷新表格
使用表格组件为proTable,通过params参数监听state对象值变化,并且在提交表格表单时会自动将params监听的值作为参数传入表格初始化的request的函数中。
proTable会在初始化时或者提交proTable自动生成的表单时触发request中的promise方法,当识别到promise方法返回内容重success属性为true时,才会将返回值渲染到表格中进行展示。
通过search api 可以对proTable的自动生成的搜索表单项进行自定义。
其中optionRender可以自定义表单按钮
rowkey一定要指定为改行数据的唯一值,否则会出现表格刷新后数据显示异常
ProColumns对象为对表格每列的定义,其中
hideInSearch 表示这列是否会自动生成检索表单
hideInTable 表示该列是否展示在表格中
render 可以控制该列显示的内容(比如需要对数据做处理后展示,或者该列为操作框)
valueType 可以定义通过该列自动生成的表单选项框的类型
const columns: ProColumns<DeploymentInformationListItem>[] = [
{
title: '环境选择',
dataIndex: 'deployEnvId',
hideInTable: true,
valueType: 'select',
formItemProps: {
rules: [
{
required: true,
message: '此项为必填项',
},
],
},
valueEnum: optionEnv,
},
{
title: 'ID',
dataIndex: 'publishId',
hideInSearch: true,
},
{
title: '操作人',
dataIndex: 'modifierName',
hideInSearch: true,
},
{
title: '操作类型',
dataIndex: 'publishType',
hideInSearch: true,
render: (_, record) => {
switch (record.publishType) {
case 0:
return '重启';
case 1:
return '正常发布';
case -1:
return '回滚';
}
},
},
{
title: '状态',
dataIndex: 'publishResult',
hideInSearch: true,
render: (_, record) => {
switch (record.publishResult) {
case -1:
return '失败';
case 0:
return '构建中';
case 1:
return '成功';
}
},
},
{
title: '代码分支',
dataIndex: 'publishGitBranch',
valueType: 'select',
valueEnum: optionBranches,
formItemProps: {
rules: [
{
required: optionType,
message: '此项为必填项',
},
],
},
},
{
title: '所属环境',
dataIndex: 'deployEnvTitle',
valueType: 'textarea',
hideInSearch: true,
},
{
title: '发布备注',
dataIndex: 'publishDesc',
valueType: 'textarea',
hideInTable: true,
},
{
title: '操作时间',
dataIndex: 'gmtModified',
valueType: 'dateTime',
hideInSearch: true,
},
{
title: '操作',
dataIndex: 'option',
valueType: 'option',
render: (_, record) => {
let result = [];
result.push(
<a
onClick={() => {
history.push({
pathname: '/deploymentmanagement/applicationdetails',
state: {
applicationDetails_appCode: props.appCode,
applicationDetails_publishId: record.publishId,
},
});
}}
>
详情
</a>,
);
if (record.publishResult === 1) {
result.push(
<a
onClick={() => {
publishRollback(record.publishId);
actionRef.current.reload();
}}
>
回滚至当前版本
</a>,
);
}
return result;
},
},
];
<ProTable<DeploymentInformationListItem, DeploymentInformationListPagination>
columns={columns}
actionRef={actionRef}
formRef={formRef}
pagination={{
defaultPageSize: 10,
}}
rowKey="publishId"
optionAppNames={false}
params={confirmAppCode}
search={{
//默认展开状态去掉"收起"选项
defaultCollapsed: false,
collapseRender: () => null,
//宽度
labelWidth: 'auto',
// 自定义table表单中的按钮信息
optionRender: (searchConfig, formProps, dom) => [
<a>当前K&S部署:</a>,
<Button
type="primary"
onClick={() => {
setOptionType(true);
isPublishOrRestart(1);
}}
>
发布
</Button>,
<Button
type="primary"
onClick={() => {
setOptionType(false);
isPublishOrRestart(0);
}}
>
重启
</Button>,
<Button
type="primary"
onClick={() => {
setOptionType(false);
tableSearch();
}}
>
搜索
</Button>,
],
}}
request={(params, sorter, filter) => DeploymentTableList({ ...params, sorter, filter })}
form={{
ignoreRules: false,
}}
/>
5.2 可编辑表格问题
初始化时展示对象必须给[],若未undefined,则整个控件无法显示
6.控制台报错问题
6.1 在componentWillMount钩子函数中不能修改state,只能有函数调用
如下例,因为在钩子函数中对state做了修改所以会导致报错
useEffect(() => {
getMenuAll(initialState.currentUser.tenantId).then((resp) => {
const treeMenus = getMenu(resp.allowPageList);
setTreeMenus(treeMenus);
});
if (props.optionType != 'add') {
init();
}
}, []);
if ('selectMenu' in props) {
props.selectMenu(allCheckedKeys);
}
修改为以下形式可以修复该报错
useEffect(() => {
initMenu()
if (props.optionType != 'add') {
init();
}
}, []);
const initMenu = () => {
getMenuAll(initialState.currentUser.tenantId).then((resp) => {
const treeMenus = getMenu(resp.allowPageList);
setTreeMenus(treeMenus);
});
}
useEffect(() => {
if ('selectMenu' in props) {
props.selectMenu(allCheckedKeys);
}
},[allCheckedKeys])
6.2 proTable自定义表单按钮时需要指定key
如下案例optionRender中的自定义按钮未指定key会导致一下报错
<ProTable<UserListItem, UserListPagination>
actionRef={actionRef}
rowKey="uid"
options={false}
pagination={{
pageSize: 10,
}}
formRef={formRef}
search={{
//默认展开状态去掉"收起"选项
defaultCollapsed: false,
collapseRender: () => null,
//宽度
labelWidth: 'auto',
optionRender: (searchConfig, formProps, dom) => [
// ...dom.reverse(),把重置和查询去掉
<Button
type="primary"
icon={<PlusOutlined />}
onClick={() =>
history.push({
pathname: '/usermanagement/user/add',
state: {
optionType: 'add',
},
})
}
>
新建用户
</Button>,
<Button
type="primary"
onClick={() => {
formRef.current.submit();
}}
>
查询
</Button>,
],
}}
request={userTableList}
columns={columns}
/>
6.3 树形控件加载顺序导致显示问题
useEffect(() => {
initMenu()
if (props.optionType != 'add') {
init()
}
}, []);
出现原因:由于initMenu为加载整个树形结构数据,但是init为加载勾选的节点,因为initMenu与init都为异步的,所以会出现加载勾选的节点加载完成的,但完整树形结构数据未加载完成,从而导致这个错误
优化后代码:
useEffect(() => {
initMenu()
}, []);
const initMenu = () => {
getMenuAll(initialState.currentUser.tenantId).then((resp) => {
const treeMenus = getMenu(resp.allowPageList);
setTreeMenus(treeMenus);
if (props.optionType != 'add') {
init()
}
});
}