[英]React Native Firebase App: Change Stack.Screen onAuthStateChange
[英]Prevent React Native remount of component on context value change
在我正在构建的聊天应用程序中,每当用户发送消息和启动聊天时,我想从用户帐户中扣除积分。 用户帐户可在应用程序中作为上下文访问,并使用 firestore 文档上的快照侦听器在用户帐户文档中发生任何更改时进行更新。 (请参阅底部的代码示例 1. 和 2. )
现在,只要 userAccount 对象中的任何内容发生变化,所有上下文提供程序子项(NavigationStructure 及其所有子组件)都会根据React 的文档重新呈现。
然而,这会在也使用此上下文的聊天屏幕上造成巨大问题:只要上下文中的某些内容发生变化,就会重新初始化那里定义的状态。 例如,我有一个标志指示模态是否可见,默认值是可见的。 当我进入聊天屏幕时,隐藏模态,在 firestore 数据库中手动更改值(例如扣除积分)聊天屏幕重新呈现并且模态再次可见。 (参见代码示例 3。 )
我很迷茫解决这个问题的最好方法是什么,有什么想法吗?
我想到的解决方案:
我尝试过的绝望的事情:
代码示例
function App() {
const userData = useUserData();
...
return (
<>
<UserContext.Provider value={{ ...userData }}>
<NavigationStructure />
</UserContext.Provider>
</>
}
export const useUserData = () => {
const [user, loading] = useAuthState(authFB);
const [userAccount, setUserAccount] = useState<userAccount | null>();
const [userLoading, setUserLoading] = useState(true);
useEffect(() => {
...
unsubscribe = onSnapshot(
doc(getFirestore(), firebaseCollection.userAccount, user.uid),
(doc) => {
if (doc.exists()) {
const data = doc.data() as userAccount & firebaseRequirement;
//STACK OVERFLOW COMMENT: data CONTAINES 'credits' FIELD
...
setUserAccount(data);
}
...
}
);
}, [user, loading]);
...
return {
user,
userAccount,
userLoading: userLoading || loading,
};
};
export const Chat = ({ route, navigation }: ChatScreenProps): JSX.Element => {
const ctx = useContext(UserContext);
const userAccount = ctx.userAccount as userAccount;
...
//modal visibility
const [modalVisible, setModalVisible] = useState(true);
// STACK OVERFLOW COMMENT: ISSUE IS HERE.
// FOR SOME REASON THIS STATE GET'S RE-INITALIZED (AS true) WHENEVER
// SOMETHING IN THE userAccount CHANGES.
...
return (
<>
...
<Modal
title={t(tPrefix, 'tasklistModal.title')}
visible={ModalVisible}
onClose={() => setModalVisible(false)}
footer={
...
}
>
....
</Modal>
...
</>)
}
这是 React 上下文的一个特征——上下文值的任何更改都会导致所有上下文的消费者重新渲染。 这在他们的文档的警告部分中有简要介绍,但在第三方博客中得到了扩展,例如: 如何使用 React Context 破坏您的应用程序的性能。
您已经尝试过作者建议的记忆化。 记忆您的组件不会阻止重新初始化,因为当您更改用户对象时,组件中的值确实会发生变化。
解决方案是使用第三方状态管理解决方案,不依赖于 Context,而是依赖于它自己的 diffing。 Redux、Zustand 和其他流行的库会进行自己的比较,以便仅重新渲染受影响的组件。
上下文实际上只推荐用于不经常更改的值,并且无论如何都需要完整的应用程序重新呈现,例如主题更改或语言选择。 尝试用“真正的”状态管理解决方案代替它。
对上下文的任何更改确实会重新呈现所有消费者组件,无论它们是否使用更改后的属性。
但它不会卸载和安装组件,这就是您的本地状态被初始化为默认值的原因。
因此,问题不在于重新呈现(这种情况很少见),而是<Chat... />
或其父组件之一由于上下文的变化而卸载。
从给出的部分代码示例中很难分辨,但我建议您查看如何使用loading
。 loading? <div>loading..</div>: <Chat... />
loading? <div>loading..</div>: <Chat... />
会导致此行为。
作为示例,这里有一个 codesandbox ,它说明了所提出的观点。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.