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

React路由/blogs/:id路径渲染报错,求排查解决思路

问题排查:React路由/blogs/:id访问时触发Blog组件类型错误

我在React项目中配置了路由,除了指向<BlogSingle />的/blogs/:id路径外,其余路由均正常运行。访问该路径时,会触发<BlogList />组件内部<Blog />组件的未捕获类型错误:Uncaught TypeError: Cannot read properties of undefined (reading 'user')。即使删除<Blog />内的<Link>组件,手动访问该路径仍会报错,疑惑为何该路由会重新渲染<Blog />组件,附上相关代码及错误信息,求排查思路。


路由配置

<Routes>
  <Route
    exact
    path="/"
    element={
      <div>
        <BlogForm />
        <BlogList />
      </div>
    }
  />
  <Route path="/blogs" element={<UserList />} />
  <Route path="/users" element={<UserList />} />
  <Route path="/users/:id" element={<User />} />
  <Route path="/blogs/:id" element={<BlogSingle />} />
</Routes>

BlogList组件代码

import { useEffect } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { initializeBlogs } from '../reducers/blogReducer'
import Blog from './Blog'

const BlogList = () => {
  const dispatch = useDispatch()

  const user = useSelector(({ login }) => {
    return login
  })

  useEffect(() => {
    dispatch(initializeBlogs())
  }, [dispatch])
  const blogs = useSelector(({ blog }) => {
    return blog
  })

  return (
    <div>
      {blogs.map((blog) => (
        <Blog key={blog.id} user={user} blog={blog}></Blog>
      ))}
    </div>
  )
}

export default BlogList

Blog组件代码

import { useState } from 'react'
import { useDispatch } from 'react-redux'
import { saveLikes, deleteBlog } from '../reducers/blogReducer'
import { Link } from 'react-router-dom'

const Blog = ({ user, blog }) => {
  const [visualForm, setVisualForm] = useState(true)
  const dispatch = useDispatch()

  const longFormVisible = { display: visualForm ? 'none' : '' }
  const shortFormVisible = { display: visualForm ? '' : 'none' }

  const toggleForm = () => {
    setVisualForm(!visualForm)
  }

  const showDelete = blog.user.id.toString() === user[0].id.toString()

  const blogStyle = {
    paddingTop: 10,
    paddingLeft: 2,
    border: 'solid',
    borderWidth: 1,
    marginBottom: 5,
  }

  return (
    <div>
      <div style={Object.assign({}, blogStyle, longFormVisible)}>
        {blog.title} {blog.author}
        <button type="submit" onClick={toggleForm}>
          hide
        </button>
        <br />
        {blog.url}
        <br />
        likes: {blog.likes}{' '}
        <button type="submit" onClick={() => dispatch(saveLikes(blog))}>
          like
        </button>
        <br />
        {blog.user.name}
        <br />
        {showDelete ? (
          <button type="submit" onClick={() => dispatch(deleteBlog(blog))}>
            delete
          </button>
        ) : (
          ''
        )}
      </div>
      <div style={Object.assign({}, blogStyle, shortFormVisible)}>
        <Link to={`blogs/${blog.id}`}>
          {blog.title} {blog.author}
        </Link>
        <button type="submit" onClick={toggleForm}>
          view
        </button>
      </div>
    </div>
  )
}

export default Blog

BlogSingle组件代码

import { useMatch } from 'react-router-dom'
import { useEffect } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { initializeUsers } from '../reducers/userReducer'
import { initializeBlogs } from '../reducers/blogReducer'

const BlogSingle = () => {
  const dispatch = useDispatch()

  useEffect(() => {
    dispatch(initializeUsers())
  }, [dispatch])

  useEffect(() => {
    dispatch(initializeBlogs())
  }, [dispatch])

  /*const users = useSelector(({ user }) => {
    return user
  })*/

  const blogs = useSelector(({ blog }) => {
    return blog
  })

  const match = useMatch('/blogs/:id')

  const blog = match
    ? blogs.find((blog) => blog.id === match.params.id.toString())
    : null
  console.log(match.params.id.toString())

  if (!blog) {
    return null
  }
  return (
    <div>
      <h1>
        {blog.title} {blog.author}
      </h1>
      <h2>added blogs</h2>
    </div>
  )
}

export default BlogSingle

错误信息

Uncaught TypeError: Cannot read properties of undefined (reading 'user')
at Blog (bundle.js:142:27)
at renderWithHooks (bundle.js:36135:22)
at mountIndeterminateComponent (bundle.js:40752:17)
at beginWork (bundle.js:42207:20)
at HTMLUnknownElement.callCallback (bundle.js:24059:18)
at Object.invokeGuardedCallbackDev (bundle.js:24108:20)
at invokeGuardedCallback (bundle.js:24170:35)
at beginWork$1 (bundle.js:47132:11)
at performUnitOfWork (bundle.js:46269:16)
at workLoopSync (bundle.js:46182:9)


排查思路与解决方案

1. 确认BlogList的渲染范围

检查App组件结构,是否在<Routes>组件外部渲染了BlogList?如果是,会导致所有路由页面都会加载BlogList组件,包括/blogs/:id路径。这种情况下,访问该路径时BlogList会被渲染,进而触发Blog组件的错误。

解决:确保BlogList只在对应路由的element内部渲染(当前路由配置的写法是正确的,重点检查App是否有额外的BlogList渲染)。

2. 给Blog组件添加空值检查

错误根源是blog.user为undefined,导致无法读取id属性。在Blog组件中对可能为空的字段添加可选链或条件判断:

// 修改showDelete的判断逻辑
const showDelete = blog?.user?.id && user?.[0]?.id 
  ? blog.user.id.toString() === user[0].id.toString()
  : false;

// 渲染用户名称时添加空值处理
{blog?.user?.name}

3. 检查Redux中博客数据的完整性

查看initializeBlogs action的处理逻辑,确保后端返回的博客数据包含完整的user字段,且在Redux reducer中正确保存了该字段。如果后端返回的博客仅包含userId而不是完整的user对象,需要在获取博客后关联用户数据。

4. 验证BlogSingle的博客匹配逻辑

BlogSingle中使用match.params.id.toString()匹配博客id,检查Redux中博客的id类型是否为字符串。如果博客id是数字类型,需要改为parseInt(match.params.id)进行匹配,避免因类型不匹配导致找不到博客,进而引发后续问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 08:05:19