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

React Router 6加载资源时如何在当前页面展示错误而非跳转

解决React Router加载错误时在当前页面展示提示的问题

核心思路

React Router的errorElement是用来处理致命路由错误(比如loader抛出异常、组件渲染崩溃)的,会触发页面跳转。要实现当前页展示非致命错误(如422),需要:

  • 在loader中不抛出非致命错误,而是将错误信息作为返回值的一部分
  • 在组件内部读取loader返回的结果,判断并展示提示组件(snackbar/提示框)

具体实现步骤

1. 修改Loader逻辑,返回错误信息而非抛出异常

把非致命错误(如422、400)的处理改为返回包含错误字段的对象,只对致命错误(如500、404)抛出异常:

async function rootLoader() {
  try {
    const res = await fetch('/api/data');
    if (!res.ok) {
      const errorData = await res.json();
      // 非致命错误:返回错误信息,不抛出
      if (res.status === 422 || res.status === 400) {
        return { error: errorData.message, status: res.status };
      }
      // 致命错误:抛出异常,触发errorElement
      throw new Response(JSON.stringify(errorData), { status: res.status });
    }
    const data = await res.json();
    return { data };
  } catch (err) {
    // 网络错误等致命情况,抛出触发errorElement
    throw err;
  }
}

2. 在组件内部处理错误并展示提示

在Root或子组件中使用useLoaderData获取结果,判断是否存在错误,然后渲染提示组件:

import { useLoaderData } from 'react-router-dom';
import { useState, useEffect } from 'react';
import Snackbar from './Snackbar'; // 自定义提示组件

function Root() {
  const loaderResult = useLoaderData();
  const [showSnackbar, setShowSnackbar] = useState(false);
  const [snackbarMessage, setSnackbarMessage] = useState('');

  useEffect(() => {
    if (loaderResult.error) {
      setSnackbarMessage(loaderResult.error);
      setShowSnackbar(true);
    }
  }, [loaderResult]);

  return (
    <div>
      {/* 页面原有内容 */}
      {loaderResult.data && <div>数据展示:{JSON.stringify(loaderResult.data)}</div>}
      {/* 错误提示组件 */}
      <Snackbar 
        open={showSnackbar}
        message={snackbarMessage}
        onClose={() => setShowSnackbar(false)}
      />
    </div>
  );
}

3. 全局提示的优化方案(可选)

如果多个组件都需要处理这类错误,可以用React Context封装提示逻辑,减少重复代码:

// SnackbarContext.js
import { createContext, useContext, useState } from 'react';

const SnackbarContext = createContext();

export function SnackbarProvider({ children }) {
  const [snackbarState, setSnackbarState] = useState({ open: false, message: '' });

  const showSnackbar = (message) => {
    setSnackbarState({ open: true, message });
  };

  const hideSnackbar = () => {
    setSnackbarState({ open: false, message: '' });
  };

  return (
    <SnackbarContext.Provider value={{ ...snackbarState, showSnackbar, hideSnackbar }}>
      {children}
      <Snackbar 
        open={snackbarState.open}
        message={snackbarState.message}
        onClose={hideSnackbar}
      />
    </SnackbarContext.Provider>
  );
}

export function useSnackbar() {
  return useContext(SnackbarContext);
}

在Root组件中使用:

function Root() {
  const loaderResult = useLoaderData();
  const { showSnackbar } = useSnackbar();

  useEffect(() => {
    if (loaderResult.error) {
      showSnackbar(loaderResult.error);
    }
  }, [loaderResult, showSnackbar]);

  return (
    <div>
      {/* 页面内容 */}
    </div>
  );
}

4. 保留errorElement处理致命错误

原来的errorElement继续保留,专门处理需要跳转错误页的致命错误:

const router = createBrowserRouter([
  {
    path: "/",
    element: <Root />,
    errorElement: <ErrorPage />, // 仅处理致命错误
    loader: rootLoader,
    children: [
      {
        path: "contacts/:contactId",
        element: <Contact />,
        // 子路由同理,loader返回错误信息,组件内部处理
        loader: async ({ params }) => {
          try {
            const res = await fetch(`/api/contacts/${params.contactId}`);
            if (!res.ok) {
              const errorData = await res.json();
              if (res.status === 422) {
                return { error: errorData.message };
              }
              throw new Response(JSON.stringify(errorData), { status: res.status });
            }
            return { data: await res.json() };
          } catch (err) {
            throw err;
          }
        }
      },
    ],
  },
]);

关键注意点

  • 严格区分致命错误和非致命错误:只有需要跳转错误页的情况才抛出异常,其余情况返回错误信息
  • 组件内部通过useLoaderData获取结果,配合状态管理展示提示
  • 全局提示用Context可以提升代码复用性,避免重复逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 15:15:35