React页面路由配置报错:元素类型无效问题求助
React页面路由报错问题排查与修复
问题场景
在起始组件Home中配置路由,访问http://localhost:3000/时触发组件类型无效的错误。
起始组件Home(src/app/page.js)
"use client"; import { useState } from "react"; import { Nav, Navbar, Sidebar } from "./views/test.js"; import "./css/app.css"; import { BrowserRouter as Router, Route, Switch, Link } from "react-router-dom"; import { BlogPost } from "./views/blog.js"; export default function Home() { return ( <Router> <div> <nav> <ul> <li> <Link to="/">Home</Link> </li> <li> <Link to="/blog">About</Link> </li> </ul> </nav> <Switch> <Route exact path="/"> <Nav/> </Route> <Route path="/blog"> <BlogPost/> </Route> </Switch> </div> </Router> ); }
报错信息
Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
Check the render method of
Home.
项目目录结构
├─node_modules ├─public ├─src │ └─app │ ├─api │ ├─css │ ├─router │ ├─views │ ├─Layout.js │ └─page.js -> func Home() ├─README.md ├─next.config.mjs ├─package-lock.json ├─postcss.config.mjs ├─jsconfig.json ├─package.json └─tailwind.config.js
涉及组件代码
BlogPost组件(src/app/views/blog.js)
import "../css/blog.css"; import { get_article_by_id } from "../api/server.js"; import remarkGfm from "remark-gfm"; import ReactMarkdown from "react-markdown"; import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; import { dark } from "react-syntax-highlighter/dist/esm/styles/prism"; function Article(props) { // console.log( "[debug] props.content is "+ typeof props.content); // console.log( "[debug] props.content : "+ props.content); return ( <div> <ReactMarkdown className="md_title" children={props.title} /> <ReactMarkdown className="md_content" children={props.content} remarkPlugins={[remarkGfm]} components={{ code(props) { const { children, className, node, ...rest } = props; const match = /language-(\w+)/.exec(className || ""); return match ? ( <SyntaxHighlighter {...rest} PreTag="div" children={String(children).replace(/\n$/, "")} language={match[1]} style={dark} /> ) : ( <code {...rest} className={className}> {children} </code> ); }, }} /> </div> ); } export async function BlogPost() { // let { id } = useParams(); let id = "101"; const article = await get_article_by_id(id); console.log(article); return ( <div className="md_main_vertical"> <div className="md_main_horizontal"> <Article title={article.title} content={article.content} /> </div> </div> ); }
Nav组件(src/app/views/test.js)
'use client' import React, { useState } from "react"; import { FaBars, FaTimes } from "react-icons/fa"; import "../css/test.css"; export function Navbar({ sidebarOpen, toggleSidebar }) { return ( <header className="navbar"> <button className="menu-button" onClick={toggleSidebar}> {sidebarOpen ? <FaTimes /> : <FaBars />} </button> <h1>Menghan Studio</h1> </header> ); } export function Sidebar({ sidebarOpen }) { return ( <div> <nav className={`sidebar ${sidebarOpen ? "open" : ""}`}> <ul> <li> <a href="#home">Home</a> </li> <li> <a href="#about">About</a> </li> <li> <a href="#services">Services</a> </li> <li> <a href="#contact">Contact</a> </li> </ul> </nav> </div> ); } export function Nav() { const [sidebarOpen, setSidebarOpen] = useState(false); const toggleSidebar = () => { setSidebarOpen(!sidebarOpen); }; return ( <nav> <Navbar sidebarOpen={sidebarOpen} toggleSidebar={toggleSidebar} /> <Sidebar sidebarOpen={sidebarOpen} /> </nav> ); }
问题原因及修复方案
1. React Router版本不兼容
React Router v6已移除Switch组件,替换为Routes;同时Route组件需通过element属性传递组件,不再支持嵌套写法。
2. 异步组件使用错误
BlogPost是异步函数组件,直接作为路由组件会触发报错,需用Suspense包裹处理加载状态,或改为同步组件。
3. 修复后的Home组件代码
"use client"; import { useState, Suspense } from "react"; import { Nav, Navbar, Sidebar } from "./views/test.js"; import "./css/app.css"; import { BrowserRouter as Router, Routes, Route, Link } from "react-router-dom"; // 懒加载异步组件并配合Suspense使用 const BlogPost = React.lazy(() => import("./views/blog.js").then(module => ({ default: module.BlogPost }))); export default function Home() { return ( <Router> <div> <nav> <ul> <li> <Link to="/">Home</Link> </li> <li> <Link to="/blog">Blog</Link> {/* 统一文字与路径,避免混淆 */} </li> </ul> </nav> <Suspense fallback={<div>加载中...</div>}> <Routes> <Route path="/" element={<Nav/>} /> <Route path="/blog" element={<BlogPost/>} /> </Routes> </Suspense> </div> </Router> ); }
额外优化:将BlogPost改为同步组件
如果不需要异步组件,可把数据获取逻辑移到useEffect中:
'use client' import { useEffect, useState } from "react"; import "../css/blog.css"; import { get_article_by_id } from "../api/server.js"; import remarkGfm from "remark-gfm"; import ReactMarkdown from "react-markdown"; import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; import { dark } from "react-syntax-highlighter/dist/esm/styles/prism"; function Article(props) { return ( <div> <ReactMarkdown className="md_title" children={props.title} /> <ReactMarkdown className="md_content" children={props.content} remarkPlugins={[remarkGfm]} components={{ code(props) { const { children, className, node, ...rest } = props; const match = /language-(\w+)/.exec(className || ""); return match ? ( <SyntaxHighlighter {...rest} PreTag="div" children={String(children).replace(/\n$/, "")} language={match[1]} style={dark} /> ) : ( <code {...rest} className={className}> {children} </code> ); }, }} /> </div> ); } export function BlogPost() { const [article, setArticle] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { const fetchArticle = async () => { let id = "101"; const data = await get_article_by_id(id); setArticle(data); setLoading(false); }; fetchArticle(); }, []); if (loading) return <div>加载中...</div>; if (!article) return <div>文章未找到</div>; return ( <div className="md_main_vertical"> <div className="md_main_horizontal"> <Article title={article.title} content={article.content} /> </div> </div> ); }
内容的提问来源于stack exchange,提问作者StudioMenghan
相关产品推荐
相关产品推荐

