因此,我已经建立了一个演示网站与提交新用户的形式.

文件:

App.jsx:

import React from 'react';
import './App.css';
import {
  RouterProvider,
  createBrowserRouter
} from 'react-router-dom';
//components
import Home from './pages-views/Home.jsx';
import AboutUs from './pages-views/AboutUs.jsx';
import Products from './pages-views/Products.jsx';
import Product from './pages-views/Product.jsx';
import Users from './pages-views/Users.jsx';
import UsersError from './pages-views/UsersError.jsx';
import User from './pages-views/User.jsx';
import UserError from './pages-views/UserError.jsx';
import NewUser from './pages-views/NewUser.jsx';
import ContactUs from './pages-views/ContactUs.jsx';
//layouts
import RootLayout from './components/RootLayout.jsx';
import ErrorLayout from './components/ErrorLayout.jsx';
//loaders
import { loadUsers, loadUser } from './loaders/loaders.jsx';
//actions
import { validateAndSubmitNewUser } from './actions/validateAndSubmitNewUser.jsx';

const myRouter = createBrowserRouter([
  {
    path: '/',
    element: <RootLayout />,
    //catches every error on every child Route that NOT HAVE a errorElement property
    errorElement: <ErrorLayout />,
    children: [
      {
        path: '/',
        element: <Home />,
      },
      {
        path: '/about-us',
        element: <AboutUs />,
      },
      {
        path: '/products',
        element: <Products />,
      },
      {
        path: '/product/:id',
        element: <Product />,
      },
      {
        path: '/users',
        element: <Users />,
        loader: loadUsers,
        errorElement: <UsersError />,
      },
      {
        path: '/user/:id',
        element: <User />,
        loader: loadUser,
        //errorElement: <UserError />,
      },
      {
        action: validateAndSubmitNewUser,
        path: '/new-user',
        element: <NewUser />,
      },
      {
        path: '/contact-us',
        element: <ContactUs />,
      },
    ],
  },
]);

export default function App() {
  return (
    <>
      <RouterProvider router={myRouter} />
    </>
  );
}

MainBody.jsx:

import { Outlet, useRouteError } from 'react-router-dom';

export default function MainBody() {

  let error = useRouteError();
  
  const isError = (error != null);
  console.log('is Error? ', isError);
  console.log(error);
  
  return (
    <>
      <Outlet />
      {isError ? (
        <>
          <p>Error details:</p>
          {/*printed when I throw new Error('User not found') on loader*/}
          <p>{error.name}</p>
          <p>{error.message}</p>
          <p>{error.stack}</p>
          <p>----------</p>
        </>
      ) : (
        <p></p>
      )}
    </>
  );
}

用户.jsx:

import { Link, useLoaderData, useNavigation } from 'react-router-dom';
import UserCard from '../components/UserCard.jsx';

export default function Users() {
  
  let pageState = useNavigation().state; //return idle | submitting | loading as value
  console.log(pageState);

  const users = useLoaderData();

  return (
    pageState === 'loading' ? (
      <>
        <p>Products page</p>
        <p>Loading....</p>
      </>
    ) : (
      <>
        <p>This is Users page</p>
        <Link
          to="/new-user"
          class="btn btn-outline-primary"
          style={{ margin: '8px' }}
        >
          Add new User
        </Link>
        {users.map((user) => (
          <Link to={'/user/' + user.id} key={user.id}>
            <UserCard name={user.name} userData={user} />
          </Link>
        ))}
      </>
    )
  );
}

ValiateAndSubmitNewUser.js(这是路由路径"/new-user"的操作)

export async function validateAndSubmitNewUser({ params, request }) {
    
    let formUserData = await request.formData();
    const user = {
        name,
        username,
        email,
        street,
        streetNumber,
        city,
        zipcode,
        phone,
        website,
    };
    
    user.name = formUserData.get('name');
    user.username = formUserData.get('username');
    user.email = formUserData.get('email');
    user.street = formUserData.get('street');
    user.streetNumber = formUserData.get('street-number');
    user.city = formUserData.get('city');
    user.zipcode = formUserData.get('zipcode');
    user.phone = formUserData.get('phone');
    user.website = formUserData.get('website');

    //variable validate will be the errors obj or null: that is what validateNewUser(user) returns
    let validate = validateNewUser(user);//works fine

    if (validate === null) {
        //just a api call to https://jsonplaceholder.typicode.com/users/ to submit user
        //returns the submited data as json
        let data = await submitNewUser(user);
    }
    
    return validate;
    
}

Jsx(以下是问题所在)

import clsx from 'clsx';
import { Form, useActionData, useNavigate } from 'react-router-dom';

export default function NewUser() {
    const navigate = useNavigate();
    const errors = useActionData();
    console.log('display errors ', errors);

    //if there is not errors redirect-navigate to /users page
    if (errors === null) {
        console.log("navigate(/users)");
        navigate("/users");//** (I think so) THIS IS RESPONSIBLE FOR THE PROBLEM-ERROR. See error stack below
    }

    return (
        <Form method="post" action="/new-user">
            <div className="input-group mb-4">
            <span className="input-group-text" id="name">
                Name
            </span>
            <input
                type="text"
                name="name"
                className={clsx('form-control', errors?.name && 'is-invalid')}
                aria-label="name input"
                aria-describedby="nameFeedback"
            />
            <div id="usernameFeedback" className="invalid-tooltip">
                {errors?.name}
            </div>
            </div>
            <div className="input-group mb-4">
            <span className="input-group-text" id="username">
                Username
            </span>
            <input
                type="text"
                name="username"
                className={clsx('form-control', errors?.username && 'is-invalid')}
                aria-label="username input"
                aria-describedby="usernameFeedback"
            />
            <div id="usernameFeedback" className="invalid-tooltip">
                {errors?.username}
            </div>
            </div>
            <div className="input-group mb-3">
            <span className="input-group-text" id="email">
                Email
            </span>
            <input
                type="text"
                name="email"
                className={clsx('form-control', errors?.email && 'is-invalid')}
                aria-label="Sizing example input"
                aria-describedby="emailFeedback"
            />
            <div id="emailFeedback" className="invalid-tooltip">
                {errors?.email}
            </div>
            </div>

            <div className="input-group mb-3">
            <span className="input-group-text" id="street">
                Street
            </span>
            <input
                type="text"
                name="street"
                className={clsx('form-control', errors?.street && 'is-invalid')}
                aria-label="Sizing example input"
                aria-describedby="streetFeedback"
            />
            <div id="streetFeedback" className="invalid-tooltip">
                {errors?.street}
            </div>
            </div>
            <div className="input-group mb-3">
            <span className="input-group-text" id="street-number">
                Number
            </span>
            <input
                type="text"
                name="street-number"
                className={clsx('form-control', errors?.streetNumber && 'is-invalid')}
                aria-label="Sizing example input"
                aria-describedby="street-number-feedback"
            />
            <div id="street-number-feedback" className="invalid-tooltip">
                {errors?.streetNumber}
            </div>
            </div>
            <div className="input-group mb-3">
            <span className="input-group-text" id="city">
                City
            </span>
            <input
                type="text"
                name="city"
                className={clsx('form-control', errors?.city && 'is-invalid')}
                aria-label="Sizing example input"
                aria-describedby="cityFeedback"
            />
            <div id="cityFeedback" className="invalid-tooltip">
                {errors?.city}
            </div>
            </div>
            <div className="input-group mb-3">
            <span className="input-group-text" id="zipcode">
                Zipcode
            </span>
            <input
                type="text"
                name="zipcode"
                className={clsx('form-control', errors?.zipcode && 'is-invalid')}
                aria-label="Sizing example input"
                aria-describedby="zipcodeFeedback"
            />
            <div id="zipcodeFeedback" className="invalid-tooltip">
                {errors?.zipcode}
            </div>
            </div>
            <div className="input-group mb-3">
            <span className="input-group-text" id="phone">
                Phone
            </span>
            <input
                type="text"
                name="phone"
                className={clsx('form-control', errors?.phone && 'is-invalid')}
                aria-label="Sizing example input"
                aria-describedby="phoneFeedback"
            />
            <div id="phoneFeedback" className="invalid-tooltip">
                {errors?.phone}
            </div>
            </div>
            <div className="input-group mb-3">
            <span className="input-group-text" id="website">
                Website
            </span>
            <input
                type="text"
                name="website"
                className={clsx('form-control', errors?.website && 'is-invalid')}
                aria-label="Sizing example input"
                aria-describedby="websiteFeeback"
            />
            <div id="websiteFeeback" className="invalid-tooltip">
                {errors?.website}
            </div>
            </div>
            <button type="submit" className="btn btn-outline-primary" name="submit">
            Submit
            </button>
        </Form>
    );
}

我想要实现的是在成功验证和提交后重定向到路径"/USERS". 现在发生的情况是,由于呈现循环(我认为是这样),浏览器内存/处理看起来会溢出

/*
Warning: Cannot update a component(`RouterProvider`) while rendering a different component(`NewUser`).
To locate the bad setState() call inside`NewUser`,
follow the stack trace as described in https://reactjs.org/link/setstate-in-render
NewUser@http://localhost:5173/src/pages-views/NewUser.jsx?t=1707325029056:22:20
RenderedRoute@http://localhost:5173/node_modules/.vite/deps/react-router-dom.js?v=6a7590ff:3550:7
Outlet@http://localhost:5173/node_modules/.vite/deps/react-router-dom.js?v=6a7590ff:3918:3
MainBody@http://localhost:5173/src/components/MainBody.jsx:21:15
RootLayout
RenderedRoute@http://localhost:5173/node_modules/.vite/deps/react-router-dom.js?v=6a7590ff:3550:7
RenderErrorBoundary@http://localhost:5173/node_modules/.vite/deps/react-router-dom.js?v=6a7590ff:3507:5
DataRoutes@http://localhost:5173/node_modules/.vite/deps/react-router-dom.js?v=6a7590ff:4660:7
Router@http://localhost:5173/node_modules/.vite/deps/react-router-dom.js?v=6a7590ff:3932:7
RouterProvider@http://localhost:5173/node_modules/.vite/deps/react-router-dom.js?v=6a7590ff:4477:7
App react-dom.development.js:86:29

​*/

推荐答案

NewUser组件中,您试图作为103的副作用直接从函数组件主体中导航.这就是"呈现不同组件(NewUser)时无法更新组件(RouterProvider)"的原因.警告.

要解决这个问题,请将副作用逻辑放入带有适当依赖项的useEffect钩子调用中,这样它现在就是一个101副作用.

示例:

export default function NewUser() {
  const navigate = useNavigate();
  const errors = useActionData();
  console.log('display errors ', errors);

  useEffect(() => {
    // if there is not errors redirect-navigate to /users page
    if (errors === null) {
      console.log("navigate(/users)");
      navigate("/users", { replace: true });
    }
  }, [errors, navigate]);

  ...

你也可以直接从route loader和actions返回/抛出redirect.如果使用此方法,则从NewUser组件中删除useNavigate钩子调用和重定向逻辑,因为现在它们都在操作中处理.

示例:

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

export async function validateAndSubmitNewUser({ params, request }) {
  const formUserData = await request.formData();
  const user = {
    name: formUserData.get('name'),
    username: formUserData.get('username'),
    email: formUserData.get('email'),
    street: formUserData.get('street'),
    streetNumber: formUserData.get('street-number'),
    city: formUserData.get('city'),
    zipcode: formUserData.get('zipcode'),
    phone: formUserData.get('phone'),
    website: formUserData.get('website'),
  };

  const validate = validateNewUser(user);

  if (validate === null) {
    // returns the submitted data as json
    const data = await submitNewUser(user);
    return redirect("/users", data);
  }
    
  return validate;
}

Javascript相关问答推荐

有Angular 的material .未应用收件箱中的价值变化

Vega中的模运算符

Redux工具包查询(RTKQ)端点无效并重新验证多次触发

Redux查询多个数据库Api reducerPath&

扫描qr code后出错whatter—web.js

从WooCommerce Checkout Country字段重新排序国家,保持国家同步

将2D数组转换为图形

如何通过使用vanilla JS限制字体大小增加或减少两次来改变字体大小

将核心模块导入另一个组件模块时存在多个主题

将内容大小设置为剩余可用空间,但如果需要,可在div上显示滚动条

Eval vs函数()返回语义

为什么客户端没有收到来自服务器的响应消息?

在JS中动态创建对象,并将其追加到HTML表中

FileReader()不能处理Firefox和GiB文件

如何使用抽屉屏幕及其子屏幕/组件的上下文?

JavaScript将字符串数字转换为整数

AG-GRIDreact 显示布尔值而不是复选框

是否设置以JavaScript为背景的画布元素?

如何在单击Search按钮时更新Reaction组件?

CSS网格使页面自动滚动