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
相关产品推荐
相关产品推荐

