You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React无法渲染Express后端PostgreSQL数据问题排查

问题:React前端无法渲染PostgreSQL返回的怪物数据

问题背景

  • Express后端运行在3100端口,http://localhost:3100/monsters可正常返回怪物数据,Postman请求返回200状态码,但前端请求返回304状态码
  • 后端控制台能打印查询到的怪物数据,但React前端仅显示表格表头,monsters数组为空

后端代码(server.js)

require("dotenv").config();
const cors = require("cors")
const db = require("./db");
// Express Application setup
const express = require("express");
const morgan = require("morgan");
const app = express();
const bodyParser = require("body-parser");
const port = process.env.PORT;
app.listen(port, () => {
    console.log(`Server is UP and running and listening on port ${port}`);
})

app.use(cors());
app.use(bodyParser.json());

// HTTP request logger middleware
app.use(morgan("dev"));

// Get All Monsters
app.get('/monsters', async  (req, res) => {
    
    const results = await db.query(`select * from monsters`, (err, results) => {
        if (!err) {
            console.log(results)
            res.send(results.rows);
        }
    });
    db.end;
})

前端相关代码

App.js

import './App.css';
import MainComponent from './components/MainComponent';
import Info from './components/Info';
import Rules from './components/Rules';
import Rulebooks from './components/Rulebooks';
import Monsters from './components/Monsters';
import Adventures from './components/Adventures';
import Contact from './components/Contact';
import { Routes, Route } from 'react-router-dom';
import MonsterInfo from './components/MonsterInfo';
import { MonsterContextProvider } from './context/MonsterContext';

function App() {
  return (
    <MonsterContextProvider>
      <div>
        <Routes>
          <Route path="/" element={<MainComponent />} />
          <Route path="/info" element={<Info />} />
          <Route path="/rules" element={<Rules />} />
          <Route path="/rulebooks" element={<Rulebooks />} />
          <Route path="/monsters" element={<Monsters />} />
          {/* <Route path="/monsters/:id" element={<MonsterInfo />} /> */}
          <Route path="/adventures" element={<Adventures />} />
          <Route path="/contact" element={<Contact />} />
        </Routes>
      </div>
    </MonsterContextProvider>
   
  )
  };

export default App;

MonsterContext.js

import React, { useState, createContext } from "react";

export const MonsterContext = createContext();

export const MonsterContextProvider = props => {
    const [monsters, setMonsters] = useState([])

    // const addMonsters = (monsters) => {
    //     setMonsters([...monsters, monsters]);
    // }
    return (
        <MonsterContext.Provider value={{ monsters: monsters, setMonsters }}>
            {props.children}
        </MonsterContext.Provider>
    )
}

MonsterFinder.js

import axios from "axios";

export default axios.create({
    baseURL: "http://192.168.0.118:3100/"
});

MonsterList.jsx

import "./Monsters.css";
import React, { useEffect, useContext } from 'react';
import MonsterFinder from "../apis/MonsterFinder";
import { useNavigate, Link } from "react-router-dom";
import { MonsterContext } from '../context/MonsterContext';

const MonsterList = (props) => {
    const { monsters, setMonsters } = useContext(MonsterContext);
    const navigate = useNavigate()

    useEffect(() => {
        const fetchData = async () => {
        try {
          const response = await MonsterFinder.get("/monsters");
           setMonsters(response.data.data.monsters);
          
        } catch (err) {}
    };
       fetchData();     
    }, []);

    return (
             
        <div className="app-container">
            <h1>Monsters</h1>
            <table>
                <thead>
                    <tr>
                        <th> </th>
                        <th>Cr</th>
                        <th>Name</th>
                        <th>Source</th>
                        <th>Type</th>
                        <th>Size</th>
                        <th>Alignment</th>
                        <th>Tag</th>
                        <th>Info</th>
                    </tr>
                </thead>
                <tbody>
                    {monsters.map((monster) => {
                        return (
                        <tr>
                            <td><img src={require(`../media/${monster.Image}`)} alt={monster.Name} height="25" width="25" /></td>
                            <td>{monster.Cr}</td>
                            <td>{monster.Name}</td>
                            <td>{monster.Source}</td>
                            <td>{monster.Type}</td>
                            <td>{monster.Size}</td>
                            <td>{monster.Alignment}</td>
                            <td>{monster.Tag}</td>
                            <td><button className="button"><Link className="links" to={`/monsterinfo/${monster.Name}`}>Info</Link></button></td>
                        </tr>
                        );
                    })}
                    
                </tbody>
            </table>
        </div>
        
    )
};

export default MonsterList

问题排查与修复方案

经排查,前端存在两处关键错误:

  1. 数据获取路径错误:后端直接返回怪物数组,但前端代码中错误使用response.data.data.monsters来获取数据,应改为response.data。
    修复后代码:
    const response = await MonsterFinder.get("/monsters");
    setMonsters(response.data);
    
  2. 字段名大小写不匹配:前端渲染时使用大写字段名(如monster.Image、monster.Name),但后端从PostgreSQL返回的是小写字段名(如monster.image、monster.name),导致无法正确读取数据。
    修复需将前端渲染代码中的字段名改为小写,示例:
    <td><img src={require(`../media/${monster.image}`)} alt={monster.name} height="25" width="25" /></td>
    <td>{monster.cr}</td>
    <td>{monster.name}</td>
    <td>{monster.source}</td>
    <td>{monster.type}</td>
    <td>{monster.size}</td>
    <td>{monster.alignment}</td>
    <td>{monster.tag}</td>
    

注:使用模拟JSON数据时可正常渲染,说明问题完全集中在前端数据处理环节,304状态码为浏览器缓存机制导致,不影响数据正常获取,修复上述两处错误后即可正常渲染表格数据。


内容的提问来源于stack exchange,提问作者ray55nl

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 00:50:23