React中,实现强制组件重新渲染,状态重置

使用背景:在一个详情弹窗组件中,引用了搜索组件,每次关闭详情弹窗后再次打开,搜索组件中的值依然显示上次输入或者选择的值。

需求:每次关闭详情弹窗以后再次打开详情弹窗,搜索组件中表单值要重置,也就是搜索组件要重新渲染,状态进行重置。

详情弹窗代码:

import React, { forwardRef, useEffect, useMemo, useState } from 'react';
import { EditOutlined } from '@ant-design/icons';
import './templateDetail.scss';
import { TemplateRecords } from '@/api/interface/msgTemplate';
import { Button, Row, Tag } from 'antd';
import { noGroups, Option } from '@/enums/search';
import SearchBar from '@/components/SearchBar';
import CustomTable from '@/components/Table';
import { cloneDeep } from 'lodash-es';
import { getStatue, ruleEnums, columnGroups } from '@/mocks/template';
import {
    delBindMeter,
    getBindMeter,
    setBindMeter,
    getWbFlowMediumClassifyEntity,
    unBindAllApis
} from '@/api/modules/alarm';
import { getIndicators } from '@/utils/rulesDict';

interface detailFormProps {
    row: TemplateRecords;
    [key: string]: any;
}
export interface CustomFormRef {
    getForm: () => any;
    setForm: (row: TemplateRecords) => void;
    clearForm: () => void;
    validateForm: () => Promise<void>;
}
const TemplateEditForm = forwardRef<CustomFormRef | undefined, detailFormProps>(
    (props: detailFormProps, ref) => {
        // 分类获类型数据
        const [indicatorsList, setIndicatorsList] = useState<Option[]>([]);
        // 已选仪表 水种 体系 评价
        const [instrumentList, setInstrumentList] = useState<any[]>([]);
        // 新增:表格数据状态管理
        const [tableData, setTableData] = useState({
            data: [],
            total: 0
        });
        // 新增:加载状态
        const [loading, setLoading] = useState(false);

        /** 表格参数及表格配置 */
        const [requestParam, setRequestParam] = useState<{
            ruleId: string;
            pageSize: number;
            pageNum: number;
            [key: string]: any;
        }>({
            ruleId: props.row?.id,
            pageSize: 10,
            pageNum: 1
        });

        // 添加一个 key 状态
        const [searchBarKey, setSearchBarKey] = useState(0);

        // 清空search表单
        const clearForm = () => {
            setRequestParam({
                ruleId: props.row?.id,
                pageSize: 10,
                pageNum: 1,
                codeOrName: '',
                belongModel: '',
                type: '',
                search: '',
                mediumClassifyId: '',
                name: ''
            });

            // 改变key强制重新渲染SearchBar
            setSearchBarKey(prev => prev + 1);
        };

        // 当 props.row变化时清空表单
        useEffect(() => {
            if (props.row?.id) {
                clearForm();
            }
        }, [props.row?.id]);

        // 获取表格数据的方法 --未绑定的仪表
        const fetchTableData = async params => {
            if (!props.row?.ruleType || !unBindAllApis[props.row?.ruleType]) return;

            setLoading(true);
            try {
                const { data: unBindData } = await unBindAllApis[props.row?.ruleType](params);
                if (unBindData) {
                    const rows = unBindData.records.map((item, index) => ({
                        no: index + 1,
                        ...item
                    }));
                    setTableData({
                        data: rows || [],
                        total: unBindData.total ?? 0
                    });
                }
            } catch (error) {
                console.error('Error fetching table data:', error);
                setTableData({ data: [], total: 0 });
            } finally {
                setLoading(false);
            }
        };

        // 表格操作改变参数
        const onParamChange = searchParams => {
            ... //代码
        };

        const getBindMeterList = () => {
            ...  //其他代码
        };

        useEffect(() => {
            getBindMeterList();
        }, [props.row]);

        // 删除
        const closeHandle = item => {
            delBindMeter(props.row?.id, item.targetId).then(res => {
                if (res) {
                    setInstrumentList(instrumentList.filter(v => v.targetId !== item.targetId));
                    fetchTableData(requestParam); // 删除后重新获取表格数据
                }
            });
        };

        // 选择对象
        const selectRow = row => {
            ...//其他代码
        };

        // 获取仪表类型
        const getIndicatorsList = async () => {
            ...//其他代码
        };

        useEffect(() => {
            console.log(props.row?.ruleType, 'props.row?.ruleType');
            setIndicatorsList([]);
            setRequestParam({
                ...requestParam,
                codeOrName: '',
                belongModel: '',
                type: '',
                search: '',
                mediumClassifyId: '',
                name: ''
            });

            getIndicatorsList();

            const newParams = {
                ...requestParam,
                ruleId: props.row?.id,
                type: props.row?.ruleType
            };
            setRequestParam(newParams);
        }, [props.row]);

        // 格式化表格数据
        const columns = useMemo(
            () =>
                cloneDeep(columnGroups[props.row?.ruleType] || []).map(item => {
                    if (item.key === 'status') {
                        item.render = (text, row) => <div>{getStatue(text)}</div>;
                    }
                    if (item.key === 'action') {
                        item.render = (text, row) => (
                            <div className="table-handle-btn">
                                <Button
                                    onClick={() => selectRow(row)}
                                    size="small"
                                    icon={<EditOutlined />}
                                >
                                    选择
                                </Button>
                            </div>
                        );
                    }
                    return item;
                }),
            [props.row?.ruleType, selectRow]
        );

        return (
            <div className="rules-template-detail">
                <Row>
                    <div>已选{ruleEnums[props.row?.ruleType]}</div>
                    <div className={'instrument-wrap'}>
                        {instrumentList.length > 0 &&
                            instrumentList.map((item, index) => (
                                <Tag
                                    style={{ marginTop: '10px' }}
                                    key={index}
                                    closeIcon
                                    onClose={e => {
                                        e.preventDefault();
                                        closeHandle(item);
                                    }}
                                >
                                    {item.targetName}
                                </Tag>
                            ))}
                    </div>
                </Row>
                <Row>
                    <div className="mb10">待选{ruleEnums[props.row?.ruleType]}</div>
                    <div style={{ width: '100%' }}>
                        <SearchBar
                            key={searchBarKey}
                            formItemList={noGroups[props.row?.ruleType]?.(indicatorsList) || []}
                            getSearchParams={onParamChange}
                            itemCol={10}
                            btnCol={10}
                            classSearch={'search-bar-wrap-rules'}
                        />
                        <CustomTable
                            tableTitle={`${ruleEnums[props.row?.ruleType]}列表`}
                            currentKey={'pageNum'}
                            columns={columns}
                            tableCondition={false}
                            rowKey="id"
                            tableData={tableData}
                            loading={loading}
                            requestParam={requestParam}
                            onParamChange={onParamChange}
                        />
                    </div>
                </Row>
            </div>
        );
    }
);
TemplateEditForm.displayName = 'TemplateEditForm';
export default TemplateEditForm;

关键代码:

// 添加一个 key 状态
        const [searchBarKey, setSearchBarKey] = useState(0);

        // 清空search表单
        const clearForm = () => {
            setRequestParam({
                ruleId: props.row?.id,
                pageSize: 10,
                pageNum: 1,
                codeOrName: '',
                belongModel: '',
                type: '',
                search: '',
                mediumClassifyId: '',
                name: ''
            });

            // 改变key强制重新渲染SearchBar
            setSearchBarKey(prev => prev + 1);
        };

        // 当 props.row变化时清空表单
        useEffect(() => {
            if (props.row?.id) {
                clearForm();
            }
        }, [props.row?.id]);

此外,也可以通过 ref 控制 SearchBar 组件重置,首先给 SearchBar 组件添加重置方法:

// SearchBar 组件中添加
export interface SearchBarRef {
    resetForm: () => void;
}

const SearchBar = forwardRef<SearchBarRef, any>((props, ref) => {
    const [form] = Form.useForm();
    
    // 暴露重置方法
    useImperativeHandle(ref, () => ({
        resetForm: () => {
            form.resetFields();
        }
    }));
    
    // ... 其他代码
});

然后在详情弹窗中使用:

const TemplateEditForm = forwardRef<CustomFormRef | undefined, detailFormProps>(
    (props: detailFormProps, ref) => {
        // 添加 SearchBar 的 ref
        const searchBarRef = useRef<SearchBarRef>(null);
        
        // 清空表单的方法
        const clearForm = () => {
            // 清空搜索参数
            setRequestParam({
                ruleId: props.row?.id,
                pageSize: 10,
                pageNum: 1,
                codeOrName: '',
                belongModel: '',
                type: '',
                search: '',
                mediumClassifyId: '',
                name: ''
            });
            
            // 清空 SearchBar 表单
            searchBarRef.current?.resetForm();
            
            // 重新获取数据
            fetchTableData({
                ruleId: props.row?.id,
                pageSize: 10,
                pageNum: 1
            });
        };

        // 当 props.row 变化时清空表单
        useEffect(() => {
            if (props.row?.id) {
                clearForm();
            }
        }, [props.row?.id]);

        // 暴露给父组件的方法
        useImperativeHandle(ref, () => ({
            getForm: () => ({/* ... */}),
            setForm: (row: TemplateRecords) => {/* ... */},
            clearForm: clearForm,
            validateForm: async () => {/* ... */}
        }));

        return (
            <div className="rules-template-detail">
                {/* ... 其他代码 */}
                <SearchBar
                    ref={searchBarRef}
                    formItemList={noGroups[props.row?.ruleType]?.(indicatorsList) || []}
                    getSearchParams={onParamChange}
                    itemCol={10}
                    btnCol={10}
                    classSearch={'search-bar-wrap-rules'}
                />
                {/* ... 其他代码 */}
            </div>
        );
    }
);

二者相比,更推荐第一种通过key强制重新渲染搜索组件,因为:

  1. 实现简单,代码改动少;

  2. 通过 React 的 key 机制自然处理重新渲染;

  3. 不需要修改 SearchBar 组件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值