Electron集成React时传递Props为何获取的是旧State?
问题分析与解决方案
一、为什么Log按钮打印旧的State?
这是React里非常典型的闭包捕获问题。当你把c函数作为props传给Card组件时,这个函数会“记住”它被创建时所在的App组件渲染周期里的cards state值。
每次点击+按钮添加新Card,App组件都会重新渲染,此时会生成一个新的c函数(它捕获了当前最新的cards),但之前已经渲染好的Card组件持有的还是它们被创建时的旧c函数——这些旧函数里的cards依然是当时的旧值,所以点击不同Card的Log按钮会打印出各自创建时刻的state。
二、修复方案:用useRef同步最新State
要让所有Log按钮都能拿到最新的cards,我们可以用useRef来存储最新的state值,因为ref的.current属性是可变的,不会受闭包捕获的限制。修改后的App.js代码如下:
import './App.css'; import Cards from './Cards/Cards' import { useState, useRef, useEffect } from 'react'; let KEY = 0 export default function App() { const [cards, setCards] = useState({}) // 创建ref来同步最新的cards state const cardsRef = useRef(cards); // 每次cards更新时,同步ref的current值 useEffect(() => { cardsRef.current = cards; }, [cards]); const c = () => { // 这里访问ref的current,拿到最新的state console.log(cardsRef.current) } const make = () =>{ const a = <Cards log={c} anID={KEY}/> const b = {...cards} b[KEY] = a KEY += 1 setCards(b) } return ( <div> <div className="time-container"> {Object.values(cards)} </div> <button className="add-button" onClick={() => make()} >+</button> <button className="" onClick={() => console.log(cards)} >-</button> </div> ); }
三、关于存储React元素的优化建议
直接把React元素存在state里并不是最优的做法,React的设计理念是数据驱动UI,state应该存储的是数据,而不是UI组件本身。
推荐的做法是:在state里存储每个Card的核心数据(比如id、内容等),渲染时再根据这些数据动态生成Card组件。这样做的好处:
- 更符合React的设计逻辑,代码可维护性更高
- 增删单个Card的功能更容易实现
- 避免因为存储组件带来的闭包、引用等问题
优化后的示例代码:
import './App.css'; import Cards from './Cards/Cards' import { useState, useRef, useEffect } from 'react'; let KEY = 0 export default function App() { // 存储卡片数据,而不是React元素 const [cardData, setCardData] = useState({}) const cardDataRef = useRef(cardData); useEffect(() => { cardDataRef.current = cardData; }, [cardData]); const log = () => { console.log(cardDataRef.current) } const addCard = () =>{ // 使用函数式更新,避免闭包问题 setCardData(prev => ({ ...prev, [KEY]: { id: KEY } })) KEY += 1 } // 新增删除单个卡片的功能 const deleteCard = (cardId) => { setCardData(prev => { const newData = {...prev} delete newData[cardId] return newData }) } return ( <div> <div className="time-container"> {/* 根据数据动态渲染Card组件 */} {Object.values(cardData).map(data => ( <Cards key={data.id} log={log} anID={data.id} onDelete={() => deleteCard(data.id)} /> ))} </div> <button className="add-button" onClick={addCard} >+</button> <button className="" onClick={() => console.log(cardData)} >-</button> </div> ); }
内容的提问来源于stack exchange,提问作者aLostSoul
相关产品推荐
相关产品推荐

