React函数组件如何确保localState写入后再执行导航?
解决useLocalStorage在页面跳转前未写入数据的问题
问题根源:React的setState是异步更新状态,useEffect会在状态更新后的下一次渲染周期执行,而navigate是同步触发页面跳转,当前组件卸载后useEffect无法执行,导致新数据没写入localStorage。
以下是三种可行的解决方法:
方案一:改造useLocalStorage Hook,支持异步等待
修改Hook,让状态更新和localStorage写入同步执行,并返回Promise供调用方等待:
import React, { useState, useEffect, useCallback } from "react"; export default function useLocalStorage<Type>( key: string, defaultValue: Type ): [Type, (value: React.SetStateAction<Type>) => Promise<void>] { const [state, setState] = useState(() => { const storageValue = localStorage.getItem(key); return storageValue === null ? defaultValue : JSON.parse(storageValue) as Type; }); // 封装支持异步的set方法 const setAndPersist = useCallback(async (value: React.SetStateAction<Type>) => { return new Promise<void>((resolve) => { setState((prevState) => { const newState = typeof value === "function" ? (value as (prev: Type) => Type)(prevState) : value; // 同步写入localStorage localStorage.setItem(key, JSON.stringify(newState)); resolve(); return newState; }); }); }, [key]); // 监听外部修改localStorage的情况(可选) useEffect(() => { const syncStorage = () => { const storageValue = localStorage.getItem(key); if (storageValue) { const parsed = JSON.parse(storageValue) as Type; if (JSON.stringify(parsed) !== JSON.stringify(state)) { setState(parsed); } } }; window.addEventListener("storage", syncStorage); return () => window.removeEventListener("storage", syncStorage); }, [key, state]); return [state, setAndPersist]; }
使用方式:
async function submitSignUp( event: React.MouseEvent<HTMLButtonElement, MouseEvent> ) { event.preventDefault(); // 等待数据写入localStorage后再跳转 await setUsers((oldUsers) => [...oldUsers, newUser]); navigate("/profile"); }
方案二:提交时手动写入localStorage
无需修改Hook,在提交函数中先手动更新localStorage,再更新状态并跳转:
function submitSignUp( event: React.MouseEvent<HTMLButtonElement, MouseEvent> ) { event.preventDefault(); const updatedUsers = [...users, newUser]; // 先手动写入localStorage localStorage.setItem("users", JSON.stringify(updatedUsers)); // 更新组件状态 setUsers(updatedUsers); // 执行跳转 navigate("/profile"); }
方案三:利用useEffect监听状态完成后跳转
通过状态标记控制跳转时机,确保localStorage写入完成后再执行跳转:
const [users, setUsers] = useLocalStorage("users", [] as User[]); const [shouldNavigate, setShouldNavigate] = useState(false); // 监听跳转标记,完成存储后执行跳转 useEffect(() => { if (shouldNavigate) { navigate("/profile"); setShouldNavigate(false); // 重置标记避免重复跳转 } }, [shouldNavigate, navigate]); function submitSignUp( event: React.MouseEvent<HTMLButtonElement, MouseEvent> ) { event.preventDefault(); setUsers((oldUsers) => [...oldUsers, newUser]); setShouldNavigate(true); // 设置跳转标记 }
内容的提问来源于stack exchange,提问作者billytheme
相关产品推荐
相关产品推荐

