React函数组件useState无法在内部函数中切换布尔状态值
问题根因
布尔值无法正常翻转是典型的React函数组件闭包陷阱,由两个细节问题共同导致:
columns配置在组件首次渲染时生成后就没有更新过,传入的buttonFollow格式化函数始终缓存着首次渲染作用域里的isPresent初始值true,点击触发时拿不到后续更新的状态。setIsPresent是异步操作,调用后立刻打印isPresent时,状态还未完成更新,拿到的永远是当前渲染周期的旧值。
类组件不存在这个问题,是因为类方法通过this.state读取状态,this始终指向最新的组件实例,不会被闭包缓存旧值。
修复方案
做三处调整即可:
- 状态更新使用函数式写法,不依赖闭包中的旧状态值
- 用
useMemo包裹columns配置,将isPresent加入依赖项,保证状态变化后列配置会重新生成,绑定的事件函数能拿到最新作用域 - 用
useEffect监听状态变化获取最新值,不要在setState调用后立刻读取状态
修复后的完整代码:
import React, { useState, useMemo, useEffect } from "react"; import BootstrapTable from "react-bootstrap-table-next"; import "bootstrap/dist/css/bootstrap.min.css"; import "react-bootstrap-table-next/dist/react-bootstrap-table2.min.css"; import { Button } from "react-bootstrap"; export default function App() { const [isPresent, setIsPresent] = useState(true); // 监听状态变化,打印最新值 useEffect(() => { console.log("当前isPresent值:", isPresent); }, [isPresent]); const onFollowChanged = () => { // 函数式更新,始终基于最新状态翻转,不受闭包旧值影响 setIsPresent(prev => !prev); }; // 列配置随isPresent变化重新生成 const columns = useMemo(() => [ { dataField: "id", text: "id", sort: true }, { dataField: "fName", text: "First Name", sort: true }, { dataField: "lName", text: "Last Name" }, { dataField: "grade", text: "Grade", sort: true }, { dataField: "present", text: "Present", formatter: () => <Button onClick={onFollowChanged}>Present</Button> } ], [isPresent]); const students = [ { id: 100, fName: "John", lName: "Doe", grade: "first" }, { id: 200, fName: "Jane", lName: "Doe", grade: "second" }, { id: 300, fName: "Peter", lName: "Paker", grade: "third" }, { id: 400, fName: "Clark", lName: "Kent", grade: "fourth" }, { id: 500, fName: "LeBron", lName: "James", grade: "fifth" } ]; return ( <div> <BootstrapTable keyField="id" data={students} columns={columns} /> </div> ); }
注意事项
所有依赖组件内部状态的配置项(比如本例中带自定义按钮渲染的表格列配置),都要保证状态更新时配置同步重新生成,否则很容易触发闭包陷阱,始终拿到旧的状态值。
内容的提问来源于stack exchange,提问作者stminion001
相关产品推荐
相关产品推荐

