React-Firebase应用接入Firestore后变慢,如何优化提升运行速度
React-Firebase Firestore卡顿优化方案
核心问题梳理
- useEffect无依赖数组,每次组件重渲染都会重新注册onSnapshot监听器,导致重复请求、内存泄漏
- 未设置onSnapshot取消订阅逻辑,组件卸载后监听器仍在后台运行占用资源
- 未使用Firestore服务端过滤,拉取整个users集合后在前端筛选customer类型用户,数据量越大性能损耗越高
- update操作逻辑冗余,每次更新用户状态都会全量拉取users集合遍历匹配,单次更新请求成本过高
- async/await使用错误,onSnapshot是流式监听器不是一次性Promise请求,await无实际作用还会导致逻辑异常
优化方案
- 给useEffect添加空依赖数组,保证仅组件挂载时注册一次监听器
- 声明onSnapshot返回的取消订阅函数,在useEffect的清理函数中调用,卸载时销毁监听器
- 用Firestore的
where()方法在服务端直接过滤typeOfUser为customer的用户,减少不必要的数据传输 - 拉取用户数据时同时保存文档ID,更新状态时直接用文档ID定位目标文档,无需全量拉取集合
- 删除不必要的async/await和冗余遍历逻辑,单次更新仅触发1次接口请求
- 修复原代码中状态变量名单复数不匹配的问题,避免意外的重渲染
优化后代码
import React, {useEffect, useState} from 'react' import firebase from 'firebase/compat' import { db } from '../firebase.js' import Navbar from './Navbar' import './styles/MasterScreen.css' const MasterScreen = ({typeOfUser}) => { const [customers, setCustomers] = useState([]) useEffect(() => { // 注册实时监听,服务端过滤customer类型用户 const unSubscribe = firebase.firestore() .collection('users') .where('typeOfUser', '==', 'customer') .onSnapshot(snapshot => { setCustomers(snapshot.docs.map(doc => ({ id: doc.id, // 保存文档ID用于后续更新 ...doc.data() }))) }) // 组件卸载时取消监听 return () => unSubscribe() }, []) // 空依赖仅挂载执行一次 function changeState(docId, state) { // 直接用文档ID定位更新,无需全量拉取 firebase.firestore().collection('users').doc(docId).update({state}) } return ( <div> <Navbar typeOfUser={typeOfUser} /> <div className="customers"> {customers.map(e => ( <div key={e.uid} className="customer"> <div className={`state ${e.state === 'go' ? 'green' : e.state === 'slow' ? 'yellow' : e.state === 'stop' ? 'red' : 'pause'}`}> <h3>CURRENT STATE</h3> </div> <div className="customerContents"> {/* 传文档ID代替uid,如果文档ID本身就是用户uid可直接传e.uid */} <button className='goOne btn' onClick={() => changeState(e.id, 'go')}>GO</button> <button className='slowOne btn' onClick={() => changeState(e.id, 'slow')}>SLOW</button> <button className='stopOne btn' onClick={() => changeState(e.id, 'stop')}>STOP</button> <button className='pauseOne btn' onClick={() => changeState(e.id, 'pause')}>PAUSE</button> </div> </div> ))} </div> </div> ) } export default MasterScreen
内容的提问来源于stack exchange,提问作者Tech49484
相关产品推荐
相关产品推荐

