React组件调用setState后需点击两次才重渲染的原因及子组件调用正常的疑问
为什么点击APPROVE按钮两次才触发组件重渲染?
我来帮你拆解这个问题的核心原因,以及给出更合理的解决方案:
问题根源:闭包陷阱 + React状态更新的异步性
你的handleApprove函数是在组件初始渲染时创建的,它捕获了当时的componentShouldUpdate状态值(初始为false)。当你第一次点击按钮时:
- 你调用了
setComponentShouldUpdate(!componentShouldUpdate),把状态改成了true - 但此时
handleApprove里的componentShouldUpdate还是闭包中保存的旧值false——因为React的状态更新是异步的,事件处理函数不会立刻拿到最新状态 - 第一次点击后组件会重渲染一次,生成新的
handleApprove函数(此时它捕获的componentShouldUpdate是true),但第一次点击触发的是旧渲染周期里的事件处理逻辑,所以不会触发你预期的useEffect重新获取数据 - 第二次点击时,新的
handleApprove函数调用setComponentShouldUpdate(!true)将状态改为false,状态再次变化,才会触发useEffect拉取最新数据,组件最终显示更新后的内容
而子组件调用setComponentShouldUpdate时,每次都是在子组件的渲染周期中拿到最新的状态值,所以不会有这个闭包陷阱,一次点击就能触发状态变化和重渲染。
更优雅的解决方案:直接操作目标状态
其实你完全不需要用componentShouldUpdate这个虚拟状态来触发重渲染,正确的做法是在审批请求完成后,直接更新vacations状态,或者重新拉取最新数据:
方案1:本地状态即时更新(推荐)
在请求完成后,直接修改本地vacations中对应项的状态,这样组件会立刻重渲染:
const handleApprove = async (e, vacationId) => { const isApprove = e.target.value === "approve"; const action = isApprove ? "approve" : "reject"; // 等待审批请求完成 await fetch(`http://localhost:8000/${action}-vacation/${vacationId}`, { method: "POST" }); // 用函数式更新获取最新的prevVacations,避免闭包问题 setVacations(prevVacations => prevVacations.map(vacation => vacation._id === vacationId ? {...vacation, status: isApprove ? "APPROVED" : "REJECTED"} : vacation ) ); };
方案2:重新拉取最新数据
如果需要确保本地数据和后端完全同步,可以在请求完成后重新调用获取数据的函数:
// 把获取数据的逻辑抽成独立函数 const fetchVacations = async () => { const response = await fetch("http://localhost:8000/get-vacations"); const data = await response.json(); setVacations(data); }; // 初始渲染时拉取数据 useEffect(() => { fetchVacations(); }, []); // 这里不需要任何依赖,初始执行一次即可 const handleApprove = async (e, vacationId) => { const action = e.target.value === "approve" ? "approve" : "reject"; await fetch(`http://localhost:8000/${action}-vacation/${vacationId}`, { method: "POST" }); // 请求完成后重新拉取最新数据 fetchVacations(); };
为什么虚拟状态的方式不推荐?
这种方式属于“绕弯路”的做法,不仅容易引入闭包陷阱(就是你遇到的两次点击问题),还会让代码逻辑变得晦涩——其他开发者看到componentShouldUpdate这个状态会困惑它的作用。直接操作业务状态(vacations)才是符合React设计理念的做法,状态变化自然会触发组件重渲染。
内容的提问来源于stack exchange,提问作者Aasem Shoshari
相关产品推荐
相关产品推荐

