React中无法通过MDBDataTable行内onClick事件更新state的问题
问题原因
你遇到的是闭包陷阱:点击「Load Table」时,loadTable函数会把包含setMe、getMe的JSX存入data状态,此时这两个函数捕获的是当时的cropName值(空字符串)。后续点击「set me!」更新cropName后,按钮绑定的还是旧的getMe函数,它依然引用着旧的闭包值,所以打印出来始终是空字符串。
解决方案
方法1:用useRef保存最新状态值
通过useRef存储cropName的最新值,绕过闭包限制:
import React, { useState, useRef, useEffect } from 'react'; import { render } from 'react-dom'; import { MDBDataTable } from 'mdbreact'; import './style.css'; import '@fortawesome/fontawesome-free/css/all.min.css'; import 'bootstrap-css-only/css/bootstrap.min.css'; import 'mdbreact/dist/css/mdb.css'; function App() { const [cropName, setCropName] = useState(''); const cropNameRef = useRef(''); const [data, setData] = useState([]); // 同步ref与state的最新值 useEffect(() => { cropNameRef.current = cropName; }, [cropName]); function setMe(e) { setCropName('Hello'); } function getMe(e) { console.log(cropNameRef.current); // 直接读取ref的最新值 } function loadTable() { setData({ columns: [ { label: 'Action', field: 'radio', sort: 'asc', width: 150 }, { label: 'Position', field: 'position', sort: 'asc', width: 270 }, { label: 'Office', field: 'office', sort: 'asc', width: 200 }, { label: 'Age', field: 'age', sort: 'asc', width: 100 }, { label: 'Start date', field: 'date', sort: 'asc', width: 150 }, { label: 'Salary', field: 'salary', sort: 'asc', width: 100 }, ], rows: [ { radio: ( <div className="field-input pt-2"> Click this <button onClick={setMe}>set me!</button> <button onClick={getMe}>get me!</button> </div> ), position: 'System Architect', office: 'Edinburgh', age: '61', date: '2011/04/25', salary: '$320', }, { position: 'Accountant', office: 'Tokyo', age: '63', date: '2011/07/25', salary: '$170', }, ], }); } return ( <div> <MDBDataTable striped bordered hover data={data} /> <button onClick={loadTable}>Load Table</button> </div> ); } render(<App />, document.getElementById('root'));
方法2:动态生成表格数据,避免闭包固化
不将表格数据存入state,而是每次渲染时动态生成,让getMe总能捕获最新的cropName:
import React, { useState } from 'react'; import { render } from 'react-dom'; import { MDBDataTable } from 'mdbreact'; import './style.css'; import '@fortawesome/fontawesome-free/css/all.min.css'; import 'bootstrap-css-only/css/bootstrap.min.css'; import 'mdbreact/dist/css/mdb.css'; function App() { const [cropName, setCropName] = useState(''); const [showTable, setShowTable] = useState(false); // 仅控制表格显示状态 function setMe(e) { setCropName('Hello'); } function getMe(e) { console.log(cropName); } // 动态生成表格数据,每次渲染都是最新的函数引用 const tableData = { columns: [ { label: 'Action', field: 'radio', sort: 'asc', width: 150 }, { label: 'Position', field: 'position', sort: 'asc', width: 270 }, { label: 'Office', field: 'office', sort: 'asc', width: 200 }, { label: 'Age', field: 'age', sort: 'asc', width: 100 }, { label: 'Start date', field: 'date', sort: 'asc', width: 150 }, { label: 'Salary', field: 'salary', sort: 'asc', width: 100 }, ], rows: [ { radio: ( <div className="field-input pt-2"> Click this <button onClick={setMe}>set me!</button> <button onClick={getMe}>get me!</button> </div> ), position: 'System Architect', office: 'Edinburgh', age: '61', date: '2011/04/25', salary: '$320', }, { position: 'Accountant', office: 'Tokyo', age: '63', date: '2011/07/25', salary: '$170', }, ], }; return ( <div> {showTable && <MDBDataTable striped bordered hover data={tableData} />} <button onClick={() => setShowTable(true)}>Load Table</button> </div> ); } render(<App />, document.getElementById('root'));
方法特点
- 方法1适合需要缓存表格数据的场景,通过ref同步最新状态;
- 方法2更简洁,从根源上避免将JSX存入state导致的闭包问题。
内容的提问来源于stack exchange,提问作者Salvino D'sa
相关产品推荐
相关产品推荐

