React项目Fetch仅触发一次/需开开发者工具加载地图标记问题排查
地图标记加载异常问题排查与解决
我正在开发一个网站,需要从MongoDB获取报告数据,根据报告位置在地图上生成标记。当前遇到的问题:
- 本地环境:仅首次打开网站时会触发Fetch请求,后续刷新不再触发
- 线上环境:有时完全不触发Fetch,只有打开开发者工具后标记才会显示
已尝试移除所有console.log、安装nocache处理缓存,但问题仍未解决。以下是相关代码:
前端Fetch代码
const [markers, setMarkers] = useState<Array<GeoReport>>([]); const fetchReports = async () => { const response = await fetch(process.env.REACT_APP_BACKEND + '/api/report'); const json = await response.json(); json.forEach((report: GeoReport) => { markers.push({ id: report.id, lat: report.lat, lng: report.lng, adress: report.adress, placeId: report.placeId, surveys: report.surveys, fileName: report.fileName, imgName: report.imgName }); }); setMarkers([...markers]); dispatch.setMap( // eslint-disable-next-line @typescript-eslint/no-explicit-any markers.reduce((acc: any, curr: any) => { (acc[curr.adress] = acc[curr.adress] || []).push(curr); return acc; }, {} as { [key: string]: GeoReport[] }), ); };
后端服务代码
import express from 'express'; import * as Colors from 'colors.ts'; import cors from 'cors'; import bodyParser from 'body-parser'; import nocache from 'nocache'; import { connectDB } from './database/db'; import { errorHandler } from './middleware/errorMiddleware'; import { FRONTEND_URL, PORT } from './utils/config'; import reportRoutes from './routes/reportRoutes'; import driveRoutes from './routes/driveRoutes'; import driveImageRoutes from './routes/driveImageRoutes'; // Apply Colors to consoleLogs Colors.colors('', ''); // Connection to DB export const db = async (): Promise<void> => { await connectDB(); }; void db(); const app = express(); app.use( bodyParser.urlencoded({ extended: true, }) ); app.use(nocache()); app.set('etag', false); const allowedOrigins = FRONTEND_URL; app.use( cors({ origin: allowedOrigins, }) ); app.use(express.json()); app.use((req, res, next) => { //console.log(req.path, req.method); next(); }); app.use('/api/report', reportRoutes); app.use('/api/drive', driveRoutes); app.use('/api/driveimage', driveImageRoutes); app.use(errorHandler); app.listen(PORT, () => console.log(`App listening on port ${PORT}`.green.underline.bold) );
路由代码(reportRoutes)
import express from 'express'; import { getAllreportsHandler, createreportHandler, getreportHandler, } from '../controllers/reportController'; const reportRoutes = express.Router(); reportRoutes.route('/').get(getAllreportsHandler).post(createreportHandler); reportRoutes.route('/:id').get(getreportHandler); export default reportRoutes;
问题原因分析
- 状态更新逻辑错误:直接修改
useState返回的原始数组markers.push(),React无法检测状态变化,导致后续刷新时组件无法触发数据更新;同时setMarkers([...markers])依赖异步更新的旧状态,容易出现数据不同步。 - Fetch缓存未彻底禁用:后端的
nocache配置可能未覆盖浏览器默认缓存策略,线上环境浏览器缓存机制更严格,导致请求被缓存拦截。 - 请求触发时机不明确:未看到
fetchReports的调用逻辑(如是否在useEffect中执行),若仅在组件首次挂载时调用,后续刷新或路由切换不会重新请求;而打开开发者工具时浏览器会自动禁用缓存,因此能获取到新数据。
解决办法
1. 修复前端状态更新逻辑
禁止直接修改useState返回的状态变量,基于接口返回数据创建新数组:
const fetchReports = async () => { try { const response = await fetch(process.env.REACT_APP_BACKEND + '/api/report', { // 显式禁用缓存 headers: { 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0' } }); if (!response.ok) throw new Error(`请求失败:${response.status}`); const json = await response.json(); // 基于接口数据生成新标记数组 const newMarkers = json.map((report: GeoReport) => ({ id: report.id, lat: report.lat, lng: report.lng, adress: report.adress, placeId: report.placeId, surveys: report.surveys, fileName: report.fileName, imgName: report.imgName })); setMarkers(newMarkers); // 使用新数组生成地图数据,避免依赖旧状态 dispatch.setMap( newMarkers.reduce((acc: any, curr: any) => { (acc[curr.adress] = acc[curr.adress] || []).push(curr); return acc; }, {} as { [key: string]: GeoReport[] }), ); } catch (err) { console.error('获取报告数据失败:', err); } };
2. 确保请求触发时机正确
在组件中使用useEffect调用fetchReports,根据需求设置依赖项:
useEffect(() => { fetchReports(); }, []); // 空数组表示仅组件挂载时执行一次;若需刷新时也执行,可添加如`location.pathname`等依赖
3. 强化后端缓存控制(可选)
在getAllreportsHandler中显式设置缓存头,确保响应不被缓存:
// 在reportController的getAllreportsHandler函数中 const getAllreportsHandler = async (req, res) => { try { const reports = await Report.find(); // 设置缓存控制头 res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate'); res.setHeader('Pragma', 'no-cache'); res.setHeader('Expires', '0'); res.status(200).json(reports); } catch (err) { res.status(500).json({ message: err.message }); } };
内容的提问来源于stack exchange,提问作者Ana Cláudia Faria
相关产品推荐
相关产品推荐

