在验证和提交表单后使用 useNavigate() 反应重定向,使用带有加载器和操作的路由

问题描述 投票:0回答:1

所以我建立了一个演示网站,并以提交新用户的形式。

文件:

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>
        ))}
      </>
    )
  );
}

validateAndSubmitNewUser.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;
    
}

NewUser.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

​*/
javascript reactjs react-router-dom
1个回答
0
投票

因为您试图通过重定向来更新状态,而渲染 NewUser 的过程会创建一个无限循环

这里使用useEffect将帮助您在组件渲染后执行更新

 export default function NewUser() {
        const navigate = useNavigate()
        const errors = useActionData()
        console.log('display errors ', errors);
    
        useEffect(() => {
            // Check if there are no errors, then navigate
            if (errors === null) {
                console.log("navigate(/users)")
                navigate("/users")
            }
        }, [errors, navigate])
    
        return (
            //the rest of you code 
        );
    }
© www.soinside.com 2019 - 2024. All rights reserved.