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

使用useParams无法展示JSON数据,请求技术排查

问题

我将书籍数据存储在blogs.json文件中,期望用户点击特定书籍封面时,动态展示对应的书评(即JSON文件中的blog字段)。但目前数据无法在UI上显示,控制台抛出错误:

Uncaught (in promise) SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON

错误截图

相关代码文件

blogs.json

[
  {
    "id": 1,
    "name": "The 48 Laws Of Power",
    "blog": "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur",
    "img": "https://i.ibb.co/RpP2LGp/48-Laws-Of-Power.jpg"
  },
  {
    "id": 2,
    "name": "Rich Dad Poor Dad",
    "blog": "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantit, sed quia consequuntur magni dolores eos qui ratione voluptatem seel illum qui dolorem eum fugiat quo voluptas nulla pariatur",
    "img": "https://i.ibb.co/8Brh2Nc/rich-Dad-Poor-Dad.jpg"
  },
  {
    "id": 3,
    "name": "Show Your Work",
    "blog": "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur",
    "img": "https://i.ibb.co/VjLBT1T/show-Your-Work.jpg"
  },
  {
    "id": 4,
    "name": "Atomic Habits",
    "blog": "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto tiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur",
    "img": "https://i.ibb.co/ss0kLQw/atomic-Habits.jpg"
  },
  {
    "id": 5,
    "name": "The Almanack Of Naval Ravikant",
    "blog": "Sed ut perspiciatis unde omnis iste natus error sur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur",
    "img": "https://i.ibb.co/87SmcFz/alamanack-naval.jpg"
  },
  {
    "id": 6,
    "name": "The 4-Hour Work Week",
    "blog": "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa qrelit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur",
    "img": "https://i.ibb.co/Pxtt4dH/4-hour-work-week.jpg"
  }
]

Books.js

import React, { useState, useEffect } from "react";
import Book from "./Book";

const Books = () => {
  const [books, setBooks] = useState([]);

  useEffect(() => {
    fetch("blogs.json")
      .then((res) => res.json())
      .then((data) => setBooks(data));
  }, []);

  return (
    <div id="bookshelf">
      <div className="font-bold text-3xl mt-24 mb-8">
        <h3>My Bookshelf</h3>
      </div>

      <div className="grid sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-10 ">
        {books.map((book) => (
          <Book key={book.id} book={book}></Book>
        ))}
      </div>
    </div>
  );
};

export default Books;

Book.js

import React from "react";
import { useNavigate } from "react-router-dom";
import "./Book.css";

const Book = ({ book }) => {
  const navigate = useNavigate();

  const navigateToBookReview = (id) => {
    navigate(`/book/${id}`);
  };

  const { id, name, img, description } = book;
  return (
    <div className="card book-container card-compact w-56 bg-base-100 shadow-2xl">
      <figure onClick={() => navigateToBookReview(id)}>
        <img className="h-full w-full" src={img} alt="Books" />
      </figure>
    </div>
  );
};

export default Book;

BookDetails.js

import React, { useEffect, useState } from "react";
import { useParams } from "react-router-dom";

const BookDetails = () => {
  const { id } = useParams();
  const [blog, setBlog] = useState({});

  useEffect(() => {
    const url = `http://localhost:3000/book/${id}`;
    fetch(url)
      .then((res) => res.json())
      .then((data) => setBlog(data));
  }, []);

  return (
    <div>
      <h2>This is book details: {id} </h2>
      <h2>{blog.name}</h2>
    </div>
  );
};

export default BookDetails;

App.js

import "./App.css";
import Navbar from "./Pages/Home/Shared/Navbar";
import { Routes, Route } from "react-router-dom";
import Home from "./Pages/Home/Home";
import NotFound404 from "./Pages/Home/Shared/NotFound404";
import BookDetails from "./Pages/Home/Books/BookDetails";

function App() {
  return (
    <div>
      <Navbar></Navbar>
      <Routes>
        <Route path="/" element={<Home></Home>} />
        <Route path="/book/:id" element={<BookDetails></BookDetails>} />
        <Route path="*" element={<NotFound404></NotFound404>}></Route>
      </Routes>
    </div>
  );
}

export default App;

问题原因

你在BookDetails.js里请求的http://localhost:3000/book/${id}是React Router定义的前端路由,不是后端API接口。当你发起这个请求时,服务器会返回你的React应用的index.html页面(内容以<!DOCTYPE html>开头),而不是JSON数据,所以调用res.json()时会解析失败,抛出语法错误。

解决方法

方法一:重新请求blogs.json并过滤对应书籍

修改BookDetails.js,直接请求本地的blogs.json文件,然后根据id找到对应的书籍数据:

import React, { useEffect, useState } from "react";
import { useParams } from "react-router-dom";

const BookDetails = () => {
  const { id } = useParams();
  const [blog, setBlog] = useState({});

  useEffect(() => {
    fetch("blogs.json")
      .then((res) => res.json())
      .then((data) => {
        // 把id转成数字,和JSON里的id类型匹配
        const targetBook = data.find(book => book.id === parseInt(id));
        setBlog(targetBook || {});
      });
  }, [id]); // 依赖id,确保id变化时重新请求

  return (
    <div>
      <h2>书籍详情:{id}</h2>
      <h2>{blog.name}</h2>
      <p>{blog.blog}</p> {/* 展示书评内容 */}
    </div>
  );
};

export default BookDetails;

方法二:通过路由状态传递书籍数据(推荐)

既然Books.js已经加载了所有书籍数据,可以在跳转到详情页时直接把对应书籍的数据通过路由状态传递过去,避免重复请求:

  1. 修改Book.js的跳转函数:
const navigateToBookReview = () => {
  // 直接把当前book对象传到路由状态里
  navigate(`/book/${id}`, { state: { book } });
};
  1. 修改BookDetails.js获取路由状态里的数据:
import React from "react";
import { useParams, useLocation } from "react-router-dom";

const BookDetails = () => {
  const { id } = useParams();
  const location = useLocation();
  // 从路由状态里取出书籍数据,没有的话用空对象
  const blog = location.state?.book || {};

  return (
    <div>
      <h2>书籍详情:{id}</h2>
      <h2>{blog.name}</h2>
      <p>{blog.blog}</p>
    </div>
  );
};

export default BookDetails;

额外小问题修正

Book.js里解构了description字段,但你的JSON里对应的字段是blog,虽然不影响当前功能,但可以改成const { id, name, img, blog } = book;保持一致。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 00:45:55