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

React状态已更新但视图不渲染问题求助

问题描述

被这个问题卡了一周,试了各种替代方案都没效果,只能来求助。我正在开发一款React竞赛创建应用,应用里有个存储大量信息的contest对象,存在state里,但修改它的属性后视图完全不刷新。现在只能靠forceUpdate()强制刷新,试过结合contest状态变量用useEffect也没用。

组件相关代码

const [ignored, forceUpdate] = useReducer(x => x + 1, 0);

const { course, contest } = useContext(ContestContext);
const [teams, setTeams] = useState([]);

const addTeam = (team) => {
    const teams = [...contest.teams, team];

    contest.teamsInc++;
    contest.teams = teams;

    setTeams(teams);

    ContestHandler.updateContest(contest);
}

const removeTeam = async (team) => {
    const teams = contest.teams.filter((team_) => {
        return team_.id !== team.id;
    });
    contest.teams = teams;
    setTeams(teams);

    ContestHandler.updateContest(contest);
}

if (contest.teams == undefined) {
    contest.teamsInc = 0;
    contest.teams = [];
    ContestHandler.updateContest(contest);
}

const start = () => {
    course.start();
    forceUpdate();
}

const stop = () => {
    course.stop();
    forceUpdate();
}

CourseThreePart类代码

export default class CourseThreePart extends Course {

constructor(contest){
    super(contest);
}

start(){
    if(this.canStart() && this.draw()){
        this.state.contest.started = true;
        this.state.contest.status = ContestHandler.STATUS_IN_PROGESS;
        //ContestHandler.updateContest(this.state.contest);
        return true;
    }
    this.clearAlerts();
    this.state.alerts = [...this.state.alerts, 
        {
        level: "danger", 
        message: "L'application ne parvient pas à lancer le concours de pétanque."
        }
    ]
    return false;
}

draw(){
    let teams = [...this.state.contest.teams];
    const freeWin = teams.length % 2;
    const parties_length = (teams.length / 2) + (freeWin ? -1 : 0);
    let parties = []

    const randomTeam = () => {
        const team = teams[Tools.random(0, teams.length - 1)];
        teams = teams.filter((team_) => {return team_.id !== team.id});
        return team;
    }

    for(let i = 0; i < parties_length; i++){
        const firstTeam = randomTeam();
        const secondTeam = randomTeam();

        const index = i;

        parties = [...parties, {
            id: index, 
            status: ContestHandler.PARTY_IN_PROGRESS, 
            firstTeam: firstTeam,
            secondTeam: secondTeam,
        }]
    }

    if(freeWin) {
        /**
         * Créer la free win
         */  
    }

    if(this.state.contest.rounds == undefined){
        this.state.contest.rounds = [];
    }
    this.state.contest.rounds = [...this.state.contest.rounds, {id: this.state.contest.rounds.length+ 1, parties: parties}];
    console.log(this.state.contest);

    return true;
}

canStart(){
    return this.state.contest.teams.length >= 8;
}

getAlerts(){
    let alerts = this.state.alerts;

    if(this.state.contest.teams.length < 8){
        alerts = [...alerts, {
            level: "danger",
            message: "Vous devez avoir minimum 8 équipes afin de lancer le concours."
        }]
    }
    if(this.canStart() && this.state.contest.teams.length % 2){
        alerts = [...alerts, {
            level: "warning",
            message: "Attention, vous aurez une équipe qui sera tirée au sac étant donné que vous n'avez pas un nombre d'équipe paire."
        }]
    }

    return alerts;
}
问题原因

核心问题是直接修改了state中对象的属性,没有创建新的对象引用。React的状态更新机制依赖引用变化——只有当state的引用发生改变时,React才会触发组件重新渲染。直接修改原对象的属性,React无法检测到这种变化,自然不会更新视图。

另外,在类组件CourseThreePart中,直接修改this.state的属性也是错误操作——类组件必须通过this.setState()方法更新state,直接修改this.state不会触发重新渲染。

解决方案

1. 组件中更新contest时创建新引用

假设你的ContestContext提供了更新contest的方法(比如setContest),修改addTeam和removeTeam方法,创建新的contest对象副本再更新:

const { course, contest, setContest } = useContext(ContestContext); // 假设Context里有setContest

const addTeam = (team) => {
    const newTeams = [...contest.teams, team];
    // 创建新的contest对象,复制原有属性并更新需要修改的部分
    const updatedContest = {
        ...contest,
        teamsInc: contest.teamsInc + 1,
        teams: newTeams
    };

    setTeams(newTeams);
    setContest(updatedContest); // 更新Context中的contest
    ContestHandler.updateContest(updatedContest);
}

const removeTeam = async (team) => {
    const newTeams = contest.teams.filter((team_) => team_.id !== team.id);
    const updatedContest = {
        ...contest,
        teams: newTeams
    };

    setTeams(newTeams);
    setContest(updatedContest);
    ContestHandler.updateContest(updatedContest);
}

// 初始化teams的逻辑也要改成创建新对象
if (!contest.teams) {
    const updatedContest = {
        ...contest,
        teamsInc: 0,
        teams: []
    };
    setContest(updatedContest);
    ContestHandler.updateContest(updatedContest);
}

2. 类组件中用this.setState()更新state

修改CourseThreePart中的start和draw方法,通过this.setState()更新state,确保创建新的对象引用:

start(){
    if(this.canStart() && this.draw()){
        // 使用函数式setState,确保基于最新的state更新
        this.setState(prevState => ({
            contest: {
                ...prevState.contest,
                started: true,
                status: ContestHandler.STATUS_IN_PROGESS
            }
        }));
        ContestHandler.updateContest(this.state.contest);
        return true;
    }
    this.clearAlerts();
    this.setState(prevState => ({
        alerts: [...prevState.alerts, 
            {
                level: "danger", 
                message: "L'application ne parvient pas à lancer le concours de pétanque."
            }
        ]
    }));
    return false;
}

draw(){
    let teams = [...this.state.contest.teams];
    const freeWin = teams.length % 2;
    const parties_length = (teams.length / 2) + (freeWin ? -1 : 0);
    let parties = []

    const randomTeam = () => {
        const team = teams[Tools.random(0, teams.length - 1)];
        teams = teams.filter((team_) => team_.id !== team.id);
        return team;
    }

    for(let i = 0; i < parties_length; i++){
        const firstTeam = randomTeam();
        const secondTeam = randomTeam();

        const index = i;

        parties = [...parties, {
            id: index, 
            status: ContestHandler.PARTY_IN_PROGRESS, 
            firstTeam: firstTeam,
            secondTeam: secondTeam,
        }]
    }

    if(freeWin) {
        /**
         * Créer la free win
         */  
    }

    // 使用setState更新rounds
    this.setState(prevState => {
        const newRounds = prevState.contest.rounds 
            ? [...prevState.contest.rounds, {id: prevState.contest.rounds.length + 1, parties: parties}] 
            : [{id: 1, parties: parties}];
        return {
            contest: {
                ...prevState.contest,
                rounds: newRounds
            }
        };
    }, () => {
        // setState是异步的,回调里才能拿到更新后的state
        console.log(this.state.contest);
    });

    return true;
}

3. 移除forceUpdate依赖

当你按照上面的方式正确更新state后,React会自动检测到引用变化并触发视图刷新,forceUpdate()就可以删掉了:

const start = () => {
    course.start();
    // forceUpdate(); 删掉这个
}

const stop = () => {
    course.stop();
    // forceUpdate(); 删掉这个
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 18:45:42