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

React Router Dom v6升级后组件无法渲染的问题求助

React Router v6 路由跳转后组件不渲染问题排查与解决

问题背景

将React应用从react-router-dom: ^5.2.0升级到^6.3.0,已遵循官方迁移指南配置,但登录后选择账户跳转至仪表盘时,路由http://localhost:3000/demoUser能正常跳转,但仪表盘视图未渲染,预期应自动跳转到http://localhost:3000/demoUser/reports/dashboard并展示对应内容。

相关代码如下:

index.js

const container = document.getElementById('root');
const root = createRoot(container);

root.render(
    <Provider store={Store}>
        <BrowserRouter>
            <Routes>
                <Route path="/*" element={<App />} />
            </Routes>
        </BrowserRouter>
    </Provider>
);
serviceWorker.unregister();

App.tsx

const App: FunctionComponent<IAppProps> = () => {
    /* 其他逻辑:useEffect、请求函数等 */
    return (
        <ThemeProvider theme={theme}>
            <ResetStyles />
            <Routes>
                {/* Public Facing */}
                <Route path="*" element={<ViewLogin />} />
                <Route path="/forgotten-password/*" element={<ViewForgottenPassword />} />
                <Route path="/user/signup" element={<ViewUserSignup />} />

                {/* User Protected */}
                <Route path="/account-selection/" element={<ViewAccountSelection />} />
                <Route path="/sign-out" element={<ViewSignOut />} />
                <Route path="/user" element={<ViewUserConfiguration />} />
                <Route path="/404" element={<ViewError404 />} />
                <Route path="/:account/" element={<ViewDashboard />} />

                {/* Error Pages */}
                <Route element={<Navigate to="/404" />} />
            </Routes>
        </ThemeProvider>
    );
};

export default App;

dashboard.tsx

const ViewDashboard = (props: any) => {
   /* 其他逻辑:useEffect、请求函数等 */
   return (
        <div className="dashboard-container">
            <Nav showSidebar={showSidebar} toggleSidebar={toggleSidebar} />

            <div className={showSidebar ? 'dashboard-view-expanded' : 'dashboard-view-collapsed'}>
                <ViewMenu sidebarExpanded={showSidebar} history={props.history} upperMenuTitle={getTitle()} />
                <div className="dashboard-view__content">
                    <Routes>
                        <Route path="/:account/" element={<Navigate to={`${account.token}/reports/dashboard`} />} />
                        <Route path="/:account/account-management/*" element={<LayoutAccountManagement />} />
                        <Route path="/:account/reports/" element={<LayoutReport />} />
                        <Route element={<Navigate to="/404" />} />
                    </Routes>
                </div>
                <ViewModal />
                <ViewNotification />
                <ViewPopup />
            </div>
        </div>
    );
};

export default memo(ViewDashboard, isEqual);

account-selection.js(render方法)

render() {
        if (this.props.user.isLoggedIn !== true) {
            return <Navigate push to="/" />;
        } else if (this.state.isLoading === true) {
            return <ViewLoadingSplash />;
        } else if (this.state.hasAccount || this.props.account.isSelected) {
            console.log('Gone through account selection');
            this.props.setDisplayMenu(true);
            return <Navigate push to={`/${this.props.account.token}`} />;
        }

        return (
            <div id="account-selection__view">
                <div className="account-selection__content">
                    <div className="account-selection__content__selection">
                        <h3>Select Account</h3>
                        <ComponentInputField
                            value={this.state.search}
                            onChange={this.onInputChange}
                            placeholder="Search accounts..."
                            type="text"
                        />
                        <ComponentList rows={this.state.visibleAccounts} onRowClick={this.onAccountClick} />
                    </div>
                </div>
                <ViewNotifications />
            </div>
        );
    }

问题分析

  1. 路由优先级错误:App.tsx中path="*"通配符路由放在了最前面,React Router v6的Routes组件会按顺序匹配路由,第一个匹配成功的路由会直接渲染,导致后续所有路由(如/account-selection/、/:account/)都无法被正确匹配。
  2. 嵌套路由路径错误:ViewDashboard中的子路由使用了绝对路径(如/:account/),但嵌套路由应基于父路由路径使用相对路径,否则无法正确关联父路由的上下文。
  3. 废弃API使用:React Router v6已移除props.history,ViewMenu中依赖该属性的逻辑会失效,需改用useNavigate钩子替代。

解决方案

1. 修正App.tsx路由顺序

将通配符路由path="*"移至所有路由最后,确保其他路由优先匹配:

const App: FunctionComponent<IAppProps> = () => {
    /* 其他逻辑 */
    return (
        <ThemeProvider theme={theme}>
            <ResetStyles />
            <Routes>
                {/* Public Facing */}
                <Route path="/forgotten-password/*" element={<ViewForgottenPassword />} />
                <Route path="/user/signup" element={<ViewUserSignup />} />

                {/* User Protected */}
                <Route path="/account-selection/" element={<ViewAccountSelection />} />
                <Route path="/sign-out" element={<ViewSignOut />} />
                <Route path="/user" element={<ViewUserConfiguration />} />
                <Route path="/404" element={<ViewError404 />} />
                <Route path="/:account/" element={<ViewDashboard />} />

                {/* 通配符路由移至最后 */}
                <Route path="*" element={<ViewLogin />} />

                {/* Error Pages */}
                <Route element={<Navigate to="/404" />} />
            </Routes>
        </ThemeProvider>
    );
};

2. 修正ViewDashboard嵌套路由

使用相对路径配置子路由,并替换props.history为useNavigate钩子:

import { useParams, useNavigate } from 'react-router-dom';

const ViewDashboard = (props: any) => {
    const { account } = useParams();
    const navigate = useNavigate();

    /* 其他逻辑 */

    // 进入仪表盘后自动跳转到目标子路由
    useEffect(() => {
        navigate('reports/dashboard');
    }, [navigate, account]);

    return (
        <div className="dashboard-container">
            <Nav showSidebar={showSidebar} toggleSidebar={toggleSidebar} />

            <div className={showSidebar ? 'dashboard-view-expanded' : 'dashboard-view-collapsed'}>
                {/* 移除history props,ViewMenu内部改用useNavigate */}
                <ViewMenu sidebarExpanded={showSidebar} upperMenuTitle={getTitle()} />
                <div className="dashboard-view__content">
                    <Routes>
                        {/* 相对路径匹配父路由/:account/ */}
                        <Route path="" element={<Navigate to="reports/dashboard" />} />
                        {/* 相对路径匹配/:account/account-management/* */}
                        <Route path="account-management/*" element={<LayoutAccountManagement />} />
                        {/* 相对路径匹配/:account/reports/ */}
                        <Route path="reports/" element={<LayoutReport />} />
                        {/* 子路由通配符,匹配未定义的子路径 */}
                        <Route path="*" element={<Navigate to="/404" />} />
                    </Routes>
                </div>
                <ViewModal />
                <ViewNotification />
                <ViewPopup />
            </div>
        </div>
    );
};

3. 调整ViewMenu逻辑

在ViewMenu组件内部替换props.history为useNavigate钩子,例如:

import { useNavigate } from 'react-router-dom';

const ViewMenu = ({ sidebarExpanded, upperMenuTitle }) => {
    const navigate = useNavigate();

    // 示例:跳转逻辑
    const handleMenuClick = (path) => {
        navigate(path);
    };

    return (
        {/* 组件内容 */}
    );
};

关键说明

  • React Router v6的嵌套路由必须使用相对路径,子路由会自动继承父路由的路径前缀。
  • Routes组件的路由顺序直接影响匹配结果,通配符路由必须放在最后。
  • v6彻底移除了history对象的props传递,所有路由跳转逻辑需通过useNavigate钩子实现。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 14:18:16