You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何避免使用useState的React函数式组件出现重复渲染问题

问题根因

  • useEffect依赖缺失:在监听selectAll的useEffect中使用了steps变量,但未将其加入依赖数组,同时未判断当前选中状态是否与目标状态一致,反复调用setSelected触发无限重渲染
  • React.memo未生效:默认仅对props做浅比较,父组件传递的onSelected等回调函数如果未用useCallback包裹,每次父组件渲染都会生成新的函数引用,导致memo跳过重渲染的逻辑失效
  • 变量拼写错误:props解构出的变量是Id,但useQuery中使用的是小写id,且use变量未在代码中定义,会引发额外的渲染异常

修复方案

  1. 补全useEffect依赖,增加状态相等性判断,避免无意义的状态更新
  2. 为React.memo自定义比较逻辑,或要求父组件用useCallback包裹传递的回调函数
  3. 修正变量拼写错误,移除临时兼容代码
  4. 补全isSelected、handleClick的逻辑,确保选中状态计算正确

修正后代码

import { isEqual } from 'lodash'; // 也可以自己实现数组相等判断逻辑

function OptimiserTableBody(props) {
    const { id, columns, send, onSelected, selectAll } = props; // 修正变量名大小写
    const [selected, setSelected] = useState([]);

    // Retrieve the data 
    const resOData = useQuery(o_data_query, {
        variables: {
            id: id,
            use: use // 注意这里的use变量需要从props或其他合法来源引入
        },
        fetchPolicy: 'cache-and-network'
    });

    // 用useMemo缓存steps,避免每次渲染生成新数组
    const steps = useMemo(() => {
        return resOData.data?.jobO?.data || [];
    }, [resOData.data]);

    useEffect(() => {
        if (selectAll === true) {
            const newSelects = steps.map((n) => n.workStepId);
            // 状态一致时不触发更新,避免重渲染
            if (!isEqual(selected, newSelects)) {
                setSelected(newSelects);
            }
        }
    }, [selectAll, steps, selected]); // 补全所有用到的依赖

    // 补全isSelected逻辑
    const isSelected = (workStepId) => {
        return selected.includes(workStepId);
    };

    // 用useCallback包裹点击回调,避免每次渲染生成新函数
    const handleClick = useCallback((event, workStepId) => {
        const newSelected = selected.includes(workStepId) 
            ? selected.filter(id => id !== workStepId)
            : [...selected, workStepId];
        setSelected(newSelected);
        onSelected?.(newSelected); // 调用父组件回调
    }, [selected, onSelected]);

    return (
        <TableBody>
            {steps.map((step, index) => {
                const isItemSelected = isSelected(step.workStepId);
                return (
                    <TableRow key={step.workStepId}>
                        <TableCell key={'select'} padding="checkbox">
                            <Checkbox 
                                checked={isItemSelected}
                                inputProps={{ 'aria-labelledby': step.workStepId }}
                                style={{color: kbaColors.grey1}}
                                onClick={(event) => handleClick(event, step.workStepId)}
                            />
                        </TableCell>
                    </TableRow>
                )
            })}
        </TableBody>
    );
}

// 自定义memo的比较逻辑,按需控制重渲染条件
export default React.memo(OptimiserTableBody, (prevProps, nextProps) => {
    return (
        prevProps.id === nextProps.id
        && prevProps.selectAll === nextProps.selectAll
        && isEqual(prevProps.columns, nextProps.columns)
        // 其他需要比较的props按需添加
    )
});

额外优化建议

  • 父组件传递onSelected、send等回调函数时,用useCallback包裹,固定函数引用,进一步降低不必要的重渲染
  • 如果表格数据量很大,可以把单独的TableRow也封装为memo组件,避免单行数据变化时整个表格重渲染

内容的提问来源于stack exchange,提问作者Neal Rogers

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.08 16:54:00