为什么搭配Recoil状态的React受保护路由第二次点击才会生效?
问题根因
Recoil的状态更新是批量异步执行的,调用setLogin(true)后不会立刻修改当前渲染周期的状态值,你紧接着执行路由跳转时,ProtectedRoute读取到的还是更新前的false,自然会触发重定向回入口页。
方案1:监听状态变化后再跳转(无持久化,适合临时解决)
修改Onboarding.ts,把路由跳转逻辑放在useEffect里,等loginState确实更新为true后再执行跳转:
const Onboarding = () => { const history = useHistory(); // 改成同时读状态和写状态 const [isLoggedIn, setLogin] = useRecoilState(loginState); // 监听登录状态变化,登录成功自动跳转 useEffect(() => { if (isLoggedIn) { history.push("/main"); } }, [isLoggedIn, history]); return ( <div className="Onboarding"> <div className="DishesContainer"> <img src={Dishes} alt="" /> </div> <div className="description"> <h2>Start Cooking</h2> <p className="p1">Let’s join our community to cook better food! </p> </div> <div className="GetStartedContainer"> <AppButton text="Get Started" onClick={() => { // 点击只修改状态,跳转交给useEffect处理 setLogin(true); }} /> </div> </div> ) };
方案2:持久化状态到localStorage(推荐,解决刷新丢失登录态问题)
如果你的应用需要用户关闭页面后再次打开仍保持登录状态,直接给atom加持久化效果即可,同时解决跳转和状态持久化两个问题:
修改store.ts:
export const loginState = atom({ key: "loginState", default: false, // 加持久化副作用 effects_UNSTABLE: [ ({ onSet, setSelf }) => { // 组件初始化时从localStorage读取上次保存的状态 const savedLoginState = localStorage.getItem("loginState"); if (savedLoginState) { setSelf(savedLoginState === "true"); } // 状态变化时自动同步到localStorage onSet((newVal) => { localStorage.setItem("loginState", String(newVal)); }) } ] });
方案3:强制同步更新状态(不推荐,应急可用)
用React官方提供的flushSync强制状态同步更新,修改后立刻跳转也能读到最新值:
修改Onboarding.ts的点击事件:
import { flushSync } from "react-dom"; // 点击事件逻辑 onClick={() => { flushSync(() => { setLogin(true); }); history.push("/main"); }}
内容的提问来源于stack exchange,提问作者livne.l
相关产品推荐
相关产品推荐

