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

React state数组存储的子组件无法触发重渲染问题求解

问题根因

把React元素实例直接存在state里属于React开发反模式,这是问题的核心原因:

  • 你创建<PlanningMonthBuild>元素并塞入inputList数组的瞬间,元素上绑定的refresh prop就被固定为创建当时的值,后续父组件refresh状态更新时,数组里已经存在的旧元素不会自动同步最新的refresh值,自然无法触发子组件的useEffect执行。
  • 只有直接写在父组件return语句里的元素,会在父组件每次重渲染时重新创建,自动拿到最新props,所以你之前把组件直接写在return里时刷新功能是正常的。
  • 代码里还有一个隐藏bug:addOneMonthNext和initPlanning中直接调用setMonth/setDate修改startForNextMonth这个Date类型的state,Date是引用类型,直接修改原对象违反React state不可变原则,会引发状态更新异常。
修复方案

不要在state里存储React元素实例,state中只保留渲染需要的原始数据(即每个月的起始日期),渲染阶段再根据原始数据动态生成子组件。这样父组件每次重渲染时,所有子组件都能拿到最新的refresh值,原有追加月份的业务逻辑完全保留。

修复后的父组件代码

import React, {useEffect, useState} from "react";
import PlanningMonthBuild from "./PlanningMonthBuild";
import './Planning.css';
import "react-datepicker/dist/react-datepicker.css";

const PlanningBuild = () => {
    const [startForNextMonth, setStartForNextMonth] = useState(new Date());
    // state仅存储每个月的起始日期纯数据,不存React元素
    const [monthList, setMonthList] = useState([]);
    const [refresh, setRefresh] = useState(false);

    useEffect(() => {
        initPlanning();
    }, []);

    const handleRefresh = () => {
        setRefresh(prev => !prev);
    }

    const initPlanning = () => {
        const currentMonthStart = new Date(startForNextMonth.getFullYear(), startForNextMonth.getMonth(), 1);
        const nextMonthStart = new Date(startForNextMonth.getFullYear(), startForNextMonth.getMonth() + 1, 1);
        setMonthList(prev => [currentMonthStart, ...prev]);
        setStartForNextMonth(nextMonthStart);
    };

    const addOneMonthNext = () => {
        // 创建新Date对象,不直接修改原state引用
        const newNextMonthStart = new Date(startForNextMonth);
        newNextMonthStart.setDate(1);
        newNextMonthStart.setMonth(newNextMonthStart.getMonth() + 1);
        setMonthList(prev => [...prev, startForNextMonth]);
        setStartForNextMonth(newNextMonthStart);
    };

    return (
        <main>
            <div className="refresh-div" onClick={handleRefresh}><i className="fa fa-refresh" aria-hidden="true"></i></div>
            <button className="myButton" onClick={addOneMonthNext}>Add Next Month</button>
            <div id="insideDiv">
                {/* 渲染阶段动态生成子组件,每次父组件重渲染都会同步最新props */}
                {monthList.map((monthStart) => (
                    <PlanningMonthBuild
                        key={`month-${monthStart.getFullYear()}-${monthStart.getMonth()}`}
                        startDate={monthStart}
                        refresh={refresh}
                    />
                ))}
            </div>
        </main>
    );
};

export default PlanningBuild;

子组件可选优化

子组件里单独存dateDebut属于冗余逻辑,直接用传入的startDate即可,同时补全请求错误处理,避免静默吞错:

import React, {useEffect, useState} from "react";
import useAxiosPrivate from "../hooks/useAxiosPrivate";
import {getFirstDayOfMonth} from "../utils/dates";

const PlanningMonthBuild = ({ startDate, refresh }) => {
    const [jsListe, setJsListe] = useState([]);
    const axiosPrivate = useAxiosPrivate();

    useEffect(() => {
        let isMounted = true;
        const controller = new AbortController();

        const fetchData = async () => {
            try {
                const response = await axiosPrivate.get(`api/planning/team?start=${getFirstDayOfMonth(startDate.getFullYear(), startDate.getMonth())}`, {
                    signal: controller.signal
                });
                if (isMounted) {
                    setJsListe(response.data);
                }
            } catch (err) {
                if (err.name !== 'CanceledError') {
                    console.error('排班数据拉取失败', err);
                }
            }
        };

        fetchData();

        return () => {
            isMounted = false;
            controller.abort();
        }

    }, [refresh, startDate, axiosPrivate])

    return (jsListe.map((day)=> {
        // 原有表格tr/td渲染逻辑保持不变即可
    }))

}

export default PlanningMonthBuild;
修复效果说明
  • 点击刷新按钮后,父组件refresh状态更新触发重渲染,所有动态生成的PlanningMonthBuild组件都会拿到最新的refresh值,自动触发useEffect重新拉取接口数据完成重渲染。
  • 追加下一月视图的交互完全保留,仅将state存储内容从React元素替换为纯数据,符合React开发最佳实践。
  • 修复了原代码直接修改Date类型state引用的问题,避免状态更新异常。
  • 用「年份+月份」作为组件key比随机时间戳更稳定,可避免不必要的子组件重复挂载。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 08:45:33