React组件state更新重渲染时props随机值未重新生成问题
React类组件重渲染时随机题目值不更新问题排查
问题场景
传入App组件的props.value1、props.value2、props.value3初始值均通过Math.floor(Math.random() * 100)生成,组件构造函数中结合三个props值计算得到this.proposedAnswer作为算术题的待验证答案。点击True/False按钮时会调用setState更新numQuestions(答题总数)、numCorrect(答对题数)两个状态值,已知setState会触发组件render方法执行,但实际每次点击按钮触发重渲染时,value1、value2、value3三个随机值并未重新生成、数值不会随按钮点击发生变化。
问题对应代码
class App extends Component { constructor(props) { super(props) this.proposedAnswer = Math.floor(Math.random() * 3) + this.props.value1 + this.props.value2 + this.props.value3; this.state = { numQuestions : 0, numCorrect : 0, } } rightanswer= ()=> { if(this.proposedAnswer === this.props.value1 + this.props.value2 + this.props.value3){ this.setState((oldstate)=> ({ numCorrect : oldstate.numCorrect +=1 , numQuestions : oldstate.numQuestions += 1 , })) }else{ this.setState((oldstate)=> ({ numQuestions : oldstate.numQuestions += 1 , })) } } falseanswer= ()=> { if(this.proposedAnswer !== this.props.value1 + this.props.value2 + this.props.value3){ this.setState((oldstate)=> ({ numCorrect : oldstate.numCorrect +=1 , numQuestions : oldstate.numQuestions += 1 , })) }else{ this.setState((oldstate)=> ({ numQuestions : oldstate.numQuestions += 1 , })) } } render() { return ( <div className="App"> <div className="game"> <h2>Mental Math</h2> <div className="equation"> <p className="text">{`${this.props.value1} + ${this.props.value2} + ${this.props.value3} = ${this.proposedAnswer}`}</p> </div> <button onClick={() => this.rightanswer()}>True</button> <button onClick={() => this.falseanswer()}>False</button> <p className="text"> Your Score: {this.state.numCorrect}/{this.state.numQuestions} </p> </div> </div> ); } } export default App;
根本原因
这个现象完全符合React类组件的执行逻辑,不存在渲染异常:
- 类组件的
constructor构造函数仅在组件首次挂载时执行1次,后续所有重渲染都不会重新触发构造函数。代码中在构造函数里赋值的this.proposedAnswer只会在组件初始化时计算一次,重渲染过程中这个值不会自动更新。 value1、value2、value3是父组件传入的props,props的更新权完全归属父组件:生成三个随机数的Math.random()逻辑写在父组件中,只有父组件自身触发重渲染时,才会重新执行随机数计算、把新值传给App组件。当前App组件内部调用setState只会触发自身重渲染,不会影响父组件的执行逻辑,父组件不重渲染,传入的props值自然不会变化。- 代码存在额外隐患:
setState的更新回调中直接通过+=修改旧状态对象oldstate的属性,违反React状态不可变原则,容易引发状态更新不同步、渲染结果不符合预期的问题。
修复方案
- 移除父组件生成随机数传props的逻辑,把题目相关的
value1、value2、value3、proposedAnswer全部放到App组件自身的state中管理。 - 封装独立的新题目生成方法,组件初始化时调用一次生成第一题,每次用户答题完成、更新分数的同时,调用该方法生成新题目更新到state,即可实现答完一题自动刷新题目的效果。
- 修正
setState写法,不要直接修改旧状态对象,基于旧值计算后返回全新的状态对象即可。
修复后的核心代码参考:
class App extends Component { // 封装新题目生成逻辑 generateQuestion = () => { const value1 = Math.floor(Math.random() * 100); const value2 = Math.floor(Math.random() * 100); const value3 = Math.floor(Math.random() * 100); return { value1, value2, value3, proposedAnswer: Math.floor(Math.random() * 3) + value1 + value2 + value3 } } constructor(props) { super(props) this.state = { numQuestions: 0, numCorrect: 0, // 初始化生成第一题 ...this.generateQuestion() } } rightanswer = () => { const { value1, value2, value3, proposedAnswer } = this.state; const isCorrect = proposedAnswer === value1 + value2 + value3; this.setState((oldstate) => ({ numCorrect: isCorrect ? oldstate.numCorrect + 1 : oldstate.numCorrect, numQuestions: oldstate.numQuestions + 1, // 答完生成新题 ...this.generateQuestion() })) } falseanswer = () => { const { value1, value2, value3, proposedAnswer } = this.state; const isCorrect = proposedAnswer !== value1 + value2 + value3; this.setState((oldstate) => ({ numCorrect: isCorrect ? oldstate.numCorrect + 1 : oldstate.numCorrect, numQuestions: oldstate.numQuestions + 1, // 答完生成新题 ...this.generateQuestion() })) } render() { // 取值从this.props改为this.state即可,其余JSX结构不变 const { value1, value2, value3, proposedAnswer } = this.state; return ( <div className="App"> <div className="game"> <h2>Mental Math</h2> <div className="equation"> <p className="text">{`${value1} + ${value2} + ${value3} = ${proposedAnswer}`}</p> </div> <button onClick={() => this.rightanswer()}>True</button> <button onClick={() => this.falseanswer()}>False</button> <p className="text"> Your Score: {this.state.numCorrect}/{this.state.numQuestions} </p> </div> </div> ); } } export default App;
内容的提问来源于stack exchange,提问作者Salah Azab
相关产品推荐
相关产品推荐

