Firestore updateDoc()调用使用旧Zustand状态问题求助
问题原因
导航栏的headerRight按钮是通过useEffect设置的,而这个useEffect的依赖数组只有navigation。当Zustand里的username更新时,useEffect不会重新执行,导致按钮绑定的handleSaveData函数闭包捕获了初始的旧状态值,所以点击时用的是旧的username。
而页面内的普通按钮是在组件render函数里渲染的,每次组件状态更新(包括Zustand的username变化)都会重新渲染,按钮会绑定最新的handleSaveData,自然能拿到最新状态。
解决办法
方法1:更新useEffect依赖,让headerRight随状态更新
先给handleSaveData加上useCallback,绑定正确的状态依赖:
const handleSaveData = useCallback(async () => { const userDocRef = doc(db, "user", user.uid); try { await updateDoc(userDocRef, { username: username }); } catch (error) { console.error(error); // 建议保留错误日志,方便排查问题 } }, [user, username]); // 状态变化时重新生成函数
然后修改useEffect的依赖数组,加入handleSaveData:
useEffect(() => { navigation.setOptions({ headerRight: () => ( <Button onPress={handleSaveData} title='Save' /> ), }); }, [navigation, handleSaveData]); // 依赖更新时重新设置导航按钮
这样每次username或user变化时,handleSaveData会重新生成,useEffect也会重新执行,更新导航栏按钮绑定的最新函数。
方法2:直接在函数内获取最新store状态
避免闭包问题的更直接方式是,不在组件顶层解构状态,而是在handleSaveData内部直接调用Zustand的getState()获取最新值:
const handleSaveData = async () => { // 直接从store拉取最新的user和username const { user, username } = useUserStore.getState(); const userDocRef = doc(db, "user", user.uid); try { await updateDoc(userDocRef, { username: username }); } catch (error) { console.error(error); } };
这种方式不需要依赖数组,不管按钮什么时候绑定,点击时都会拿到Zustand里的实时状态。
方法3:将导航按钮做成独立订阅组件
把导航栏的保存按钮封装成独立组件,在组件内部订阅Zustand状态,让组件自己响应状态变化:
// 独立的保存按钮组件 const SaveHeaderButton = ({ navigation }) => { const { user, username } = useUserStore(); const handleSave = async () => { const userDocRef = doc(db, "user", user.uid); try { await updateDoc(userDocRef, { username: username }); navigation.goBack(); // 可选:保存后返回上一页 } catch (error) { console.error(error); } }; return <Button onPress={handleSave} title='Save' />; }; // 在View.js中使用 useEffect(() => { navigation.setOptions({ headerRight: () => <SaveHeaderButton navigation={navigation} />, }); }, [navigation]);
这个组件会自动订阅Zustand状态,状态变化时重新渲染,完全不用担心闭包导致的旧值问题。
内容的提问来源于stack exchange,提问作者Joe
相关产品推荐
相关产品推荐

