在 react 函数组件中,通过 useeffect 监听 mqtt 消息并动态向 state 数组追加元素时,若直接引用 state 变量会导致状态“重置”为初始值——这是由闭包捕获过期 state 引起的经典问题,需使用函数式更新避免。
React 的 useState Hook 在每次渲染中都会创建新的 state 值,而 useEffect 的回调函数在组件首次挂载时被定义并闭包捕获了当时的 stateMeasurements 值(即空数组 [])。由于该 effect 未将 stateMeasurements 列为依赖项(且不应添加,否则会引发重复订阅和内存泄漏),后续所有 setStateMeasurements([…stateMeasurements, payload.message]) 实际都基于这个“冻结”的初始值进行展开,导致每次更新都等价于 […[], newItem],看似新增,实则丢失历史累积。
✅ 正确解法是采用 函数式更新(functional update):传入一个接收前一状态作为参数的 updater 函数,确保每次更新都基于最新 state 计算:
const [stateMeasurements, setStateMeasurements] = useState([]);
useEffect(() => {
const handleMessage = (topic, message) => {
try {
const parsedMessage = JSON.parse(message.toString());
const payload = { topic, message: parsedMessage };
// ✅ 使用函数式更新,避免闭包中 state 过期
setStateMeasurements(prev => [...prev, payload.message]);
} catch (e) {
console.error("Failed to parse MQTT message:", e);
}
};
mqttClient.on('message', handleMessage);
// ✅ 清理函数:取消订阅,防止内存泄漏和重复触发
return () => {
mqttClient.off('message', handleMessage);
};
}, []); // 依赖数组为空,仅在挂载/卸载时执行
⚠️ 注意事项: