React类组件改写为函数组件后运行结果异常问题排查
问题根因
类组件与函数组件的状态更新机制存在本质区别:
- 类组件的
this.setState()接收对象参数时,会自动将新对象和原有state做浅合并,新对象里没写的字段会原样保留 - 函数组件的
useState更新方法是直接用新值整体替换旧状态,不会做自动合并,新对象里没覆盖到的旧字段会直接丢失
对照运行日志就能定位到差异来源:点击按钮2时,calculate方法返回的新状态只有{ next: "2" }:
- 类组件更新后,之前存的
total: "1"、operation: "+"不会丢失,点击等号时calculate能拿到完整的运算上下文,正常算出结果3 - 函数组件直接执行
setState(newValue)后,total和operation字段直接被清空,点击等号时calculate拿不到之前的运算符和第一个运算数,只能返回空对象。
修复方法
更新状态时手动合并新旧状态即可,推荐用函数式更新的写法,同时规避闭包拿到旧状态值的问题:
把原来的handleClick方法改成下面这样:
const handleClick = (buttonName) => { setState(prevState => { const newValue = calculate(prevState, buttonName); console.log(newValue); // 手动保留旧状态里没被更新的字段 return { ...prevState, ...newValue }; }) };
修复后可正常运行的完整函数组件代码:
import { useState } from "react"; import { Display } from "./Display"; import { ButtonPanel } from "./ButtonPanel"; import calculate from "../logic/calculate"; import "./App.css"; function App2() { const [state, setState] = useState({ total: null, next: null, operation: null, }); const handleClick = (buttonName) => { setState(prevState => { const newValue = calculate(prevState, buttonName); console.log(newValue); return { ...prevState, ...newValue }; }); }; return ( <div className="component-app"> <Display value={state.next || state.total || "0"} /> <ButtonPanel clickHandler={handleClick} /> </div> ); } export default App2;
补充说明:原写法直接取渲染层闭包里的
state传给calculate,在快速连续点击场景下可能拿到过期的状态值,用setState回调里的prevState参数取最新状态可以彻底避免这个问题。
内容的提问来源于stack exchange,提问作者jinzhou Li
相关产品推荐
相关产品推荐

