React 获取数据后state数组更新但页面不渲染问题求助
问题原因
- Firebase
onSnapshot是异步回调机制:你写的getBarcodesByUser虽然标记了async,但onSnapshot本身不会返回Promise,函数执行到return result的时候,onSnapshot的回调还没触发,所以返回的永远是空数组。 - 数组引用未变更导致React无法检测更新:就算后续
onSnapshot回调往原来的result数组里push了数据,你之前setState已经把这个数组的引用存在state里了,修改原数组内容不会触发React的重渲染逻辑,forceUpdate也没用因为你调用的时候数据还没更新。
解决方案
根据是否需要实时数据更新,可选择以下两种方案:
方案1:不需要实时更新,仅拉取一次数据
把getBarcodesByUser里的onSnapshot换成一次性查询的getDocs即可:
export const getBarcodesByUser = async ( uid: string ): string[] => { const result = []; const q = query(collection(firestore, "qrcode"), where("uid", "==", uid)); // 用getDocs做一次性查询,支持async/await等待结果返回 const querySnapshot = await getDocs(q); querySnapshot.forEach( document => { result.push( document.id ) }) return result; }
修改后原有组件逻辑无需调整,即可正常拿到数据渲染。
方案2:需要保留onSnapshot的实时更新能力
不能把实时监听器当成普通异步Promise函数使用,需要在回调中直接更新组件state,同时要注意组件卸载时清理监听器避免内存泄漏:
首先调整getBarcodesByUser实现:
export const getBarcodesByUser = ( uid: string, callback: (ids: string[]) => void ) => { const q = query(collection(firestore, "qrcode"), where("uid", "==", uid)); // 返回取消监听的方法供组件卸载时调用 return onSnapshot(q, (querySnapshot) => { const result = []; querySnapshot.forEach( document => { result.push( document.id ) }) callback(result); }) }
再调整组件调用逻辑:
class MyAttendees extends React.Component { static contextType = AuthContext; // 存储取消监听的方法 unsubscribeSnapshot = null; constructor(props){ super(props); this.state = { barcodesData: [] } } componentDidMount() { const { currentUser } = this.context; const uid = currentUser.uid; // 传入回调,拿到数据后直接更新state,新的数组引用会自动触发重渲染 this.unsubscribeSnapshot = getBarcodesByUser(uid, (data) => { this.setState({ barcodesData: data }) }) } // 组件卸载时清理监听器 componentWillUnmount() { if(this.unsubscribeSnapshot) this.unsubscribeSnapshot(); } render() { return ( <div> { this.state.barcodesData.map((item) => { return <h1 key={item}>{item}</h1> }) } </div> ) } } export default MyAttendees;
该方案不仅能解决首次渲染问题,后续Firestore中对应数据发生变更时,组件也会自动同步更新。
内容的提问来源于stack exchange,提问作者Ritik Ranjan
相关产品推荐
相关产品推荐

