ReactJS onClick事件后无需刷新触发组件重渲染最佳实践
问题原因
点击预约按钮后页面不自动更新的核心原因有两个:
- 你用来判断按钮样式的
myBookings()、occupiedSlots()返回值不是React响应式状态,数据变化时React无法感知,不会触发组件重渲染,只有刷新页面重新拉取全量数据时才会拿到最新值更新视图 - 现有
canBookSlot()方法只发了POST请求提交预约,没有在提交成功后同步更新本地的预约数据,视图层拿不到最新的预约状态
另外你的渲染逻辑里还有一个隐藏的id错位bug:循环中你先执行let buttonId = id++,后续判断时却用includes(id)做匹配,后置自增运算符会导致判断用的id和按钮实际id差1,很容易出现样式匹配错误。
符合React最佳实践的修复方案
核心思路是用React状态驱动视图更新,不要依赖非响应式的普通变量/函数返回值控制渲染。
- 首先把预约数据改为组件响应式状态,组件初始化时拉取初始数据
// 在组件顶部引入需要的hook import { useState, useEffect } from 'react'; // 声明响应式状态 const [myBookingsList, setMyBookingsList] = useState([]); const [occupiedSlotsList, setOccupiedSlotsList] = useState([]); // 封装拉取最新预约数据的方法 const fetchBookingData = async () => { // 这里替换为你原来调用myBookings、occupiedSlots的接口请求逻辑 // const myRes = await Axios.get('xxx/myBookings') // const occupiedRes = await Axios.get('xxx/occupiedSlots') // setMyBookingsList(myRes.data.result) // setOccupiedSlotsList(occupiedRes.data.result) }; // 组件挂载时拉取初始数据 useEffect(() => { fetchBookingData(); }, []);
- 修改
canBookSlot方法,等预约请求提交成功后同步更新本地状态,触发自动重渲染
const canBookSlot = async (slotInfo) => { if (userDetailsObj.canBook == 0) return; try { await Axios.post('http://localhost:3001/api/book/week1/ex', { room: userDetailsObj.room, id: slotInfo.id + 1, }); // 预约成功后两种更新方式二选一: // 方式1:手动追加本地状态,更新速度快,用户无感知 setMyBookingsList(prev => [...prev, slotInfo.id + 1]); // 方式2:重新拉取全量预约数据,数据和后端完全一致,适合多人同时预约的场景 // fetchBookingData(); } catch (err) { console.error('预约提交失败', err); } };
- 修改渲染逻辑的判断条件,使用响应式状态做匹配,同时修复id错位bug
把原来判断里的myBookings().result.includes(id)、occupiedSlots().result.includes(id)替换为基于状态的判断,统一使用buttonId做匹配:
const renderTableData = () => { let id = 1; return ( <tr> {days.map((val) => ( <td> {timeSlot.map((time, i) => { const buttonId = id++; // 统一用buttonId匹配,修复原来的id错位问题 if (myBookingsList.includes(buttonId)) { return ( <button key={buttonId} id={buttonId} className="yellowButton"> {time} </button> ); } else if (occupiedSlotsList.includes(buttonId)) { return ( <button key={buttonId} id={buttonId} className="redButton"> {time} </button> ); } else { return ( <button key={buttonId} id={buttonId} className="greenButton" onClick={() => canBookSlot({ id: buttonId })} > {time} {buttonId} </button> ); } })} </td> ))} </tr> ); };
注意事项
- 不要用
forceUpdate、手动操作DOM修改className这类方式绕过React状态机制,会导致数据和视图不同步,后续维护成本极高 - 循环渲染的列表项记得加唯一
key属性(上面代码已经补充),避免React渲染复用出现异常 - 如果是多用户同时预约的场景,推荐预约成功后重新拉取全量数据,避免本地手动追加状态和其他用户的预约冲突
内容的提问来源于stack exchange,提问作者Max Andersson
相关产品推荐
相关产品推荐

