React路由跳转Shop页组件不显示仅刷新渲染的修复方案
问题原因
核心是写法不符合React单页应用的运行逻辑,两个直接诱因:
- 取数逻辑写在了模块顶层,仅文件首次加载时才会执行一次。React Router的站内跳转是无刷新的组件挂载/卸载,不会重新执行模块顶层代码,跳转进入Shop页面时根本不会触发数据拉取。
- 采用原生DOM操作直接往
#ShopCards节点插元素的方式,完全脱离React生命周期管控。就算手动触发取数,异步数据返回时如果Shop组件还没完成挂载,document.getElementById("ShopCards")会拿到null,元素自然插不进去。手动刷新时全页面重载,异步回调的触发时机刚好赶上DOM渲染完成,所以能正常显示,属于时序巧合的不稳定写法。
修复步骤
彻底抛弃手动操作DOM的思路,把数据交给React状态管理,取数逻辑放到Shop组件的挂载生命周期里执行,由React负责DOM渲染。
- 首先拆分Firebase初始化文件,移除全局数组、顶层监听调用:
// src/firebaseConfig.js import { initializeApp } from "firebase/app"; import { getDatabase, ref } from "firebase/database"; const app = initializeApp({ apiKey: "---", authDomain: "---", databaseURL: "---", projectId: "---", storageBucket: "---", messagingSenderId: "---", appId: "---" }); export const database = getDatabase(app) export const gamesRef = ref(database, '/games') export default app;
- 重写Shop组件,在组件挂载后拉取数据,遍历状态渲染卡片,卸载时解绑监听避免内存泄漏:
// src/pages/Shop.jsx import React, { Component } from 'react'; import { onValue, off } from "firebase/database"; import { gamesRef } from "../firebaseConfig.js"; class Shop extends Component { state = { gameList: [] } componentDidMount() { // 组件真实挂载到DOM后再绑定数据监听 this.unsubListener = onValue(gamesRef, (snapshot) => { const rawData = snapshot.val() const formatList = [] for (const key in rawData) { formatList.push({ id: key, name: rawData[key].Name, price: rawData[key].price, currency: rawData[key].currency_type, img: rawData[key].ImgUrl, desc: rawData[key].Desc }) } // 更新状态触发React自动重渲染 this.setState({ gameList: formatList }) }) } componentWillUnmount() { // 离开页面时解绑实时监听,防止内存泄漏、重复触发 off(gamesRef, this.unsubListener) } render() { return ( <div> <h2 className="text-center m-4">Games</h2> <h2 className="text-center m-4">Buying server is closed </h2> <div className="m-4" id="ShopCards"> {this.state.gameList.map(game => ( <div className="flip-container" key={game.id}> <div className="flipper"> <div className="front" style={{ backgroundImage: `url(${game.img})`, backgroundRepeat: 'no-repeat', backgroundSize: 'cover' }} /> <div className="back"> <div>{game.name}</div> <div>{game.price} {game.currency}</div> <div>{game.desc}</div> </div> </div> </div> ))} </div> </div> ); } } export default Shop;
避坑提醒
- 之前写在模块顶层的
GameName/GamePrice等全局数组必须全部删除,这类跨组件生命周期的全局变量会导致多次进入页面时数据重复累加,出现重复卡片。 - React项目中除非特殊场景,不要直接用
document.createElement、appendChild操作真实DOM,所有界面更新都应该通过修改state/props由React调度,否则很容易出现时序不匹配、DOM找不到的问题。 - Firebase的
onValue是长连接实时监听,必须在组件卸载时主动解绑,否则每次进入页面都会新增一个重复监听,造成不必要的性能消耗和数据重复。
内容的提问来源于stack exchange,提问作者Bester
相关产品推荐
相关产品推荐

