且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

以编程方式重定向到反应路由器 v6 中的路由的问题

更新时间:2022-10-28 07:50:44

问题

TypeError:无法读取未定义的属性(读取推送")

这是因为您尝试从不存在的 navigate 道具导航,它是未定义的.

this.props.navigate.push("/");

useNavigate 钩子仅与功能组件兼容,因此您希望/需要将 navigate 与类组件一起使用,您必须转换 AddContacts 到函数组件,或滚动您自己的自定义 withRouter 高阶组件以注入路由道具";就像 react-router-dom v5.x 中的 withRouter HOC 一样.

解决方案

我不会介绍将类组件转换为函数组件.这是一个示例自定义 withRouter HOC:

const withRouter = WrappedComponent =>道具=>{常量导航 = useNavigate();//等等...其他 react-router-dom v6 钩子返回 (

并用新的 HOC 装饰 AddContacts 组件.

export default withRouter(AddContacts);

现在这会将 navigate 道具(和您设置的任何其他道具)传递给装饰组件,并且 this.navigate 现在将是已定义.

此外,导航 API 从 v5 更改为 v6,不再使用直接的 history 对象.navigate 是一个函数而不是一个对象.要使用您调用函数并传递 1 或 2 个参数,第一个是目标路径,第二个是可选的选项";带有 replace 和/或 state 键/值的对象.

接口 NavigateFunction {(到:到,选项?:{替换?:布尔值;状态?:状态 }): 空白;(增量:数字):无效;}

现在如下导航:

this.props.navigate("/");

I want to perform navigation on certain user actions, say onSubmit of a button. suppose a user clicks on the Add contact button I want react-router to redirect in "/" which is the home page. At the moment I am facing this problem--> TypeError: Cannot read properties of undefined (reading 'push'). As a beginner, I would really appreciate experts' help.

AddContacts.js

import React, { Component } from "react";
import { Consumer } from "../../context";
import TextInputGroup from "../layout/TextInputGroup";
import { v4 as uuidv4 } from "uuid";
import { useNavigate } from "react-router-dom";

class AddContacts extends Component {
  state = {
    name: "",
    email: "",
    phone: "",
    errors: {},
  };
  onSubmit = (dispatch, e) => {
    e.preventDefault();

    const { name, email, phone } = this.state;

    //Check for errors

    if (name === "") {
      this.setState({ errors: { name: "Name is required" } });
      return;
    }
    if (email === "") {
      this.setState({ errors: { email: "Email is required" } });
      return;
    }
    if (phone === "") {
      this.setState({ errors: { phone: "Phone is required" } });
      return;
    }

    const newContact = {
      id: uuidv4(),
      name,
      email,
      phone,
    };
    dispatch({ type: "ADD_CONTACT", payload: newContact });

    this.setState({
      name: "",
      email: "",
      phone: "",
      errors: {},
    });
    this.props.navigate.push("/");
  };

  onChange = (e) => this.setState({ [e.target.name]: e.target.value });
  render() {
    const { name, email, phone, errors } = this.state;

    return (
      <Consumer>
        {(value) => {
          const { dispatch } = value;

          return (
            <div className="card mb-3">
              <div className="card-header">Add Contacts</div>
              <div className="card-body">
                <form onSubmit={this.onSubmit.bind(this, dispatch)}>
                  <TextInputGroup
                    label="Name"
                    name="name"
                    placeholder="Enter Name..."
                    value={name}
                    onChange={this.onChange}
                    error={errors.name}
                  />
                  <TextInputGroup
                    label="Email"
                    name="email"
                    type="email"
                    placeholder="Enter Email..."
                    value={email}
                    onChange={this.onChange}
                    error={errors.email}
                  />
                  <TextInputGroup
                    label="Phone"
                    name="phone"
                    placeholder="Enter Phone..."
                    value={phone}
                    onChange={this.onChange}
                    error={errors.phone}
                  />
                  <input
                    type="submit"
                    value="Add Contact"
                    className="btn btn-light btn-block mt-3"
                  />
                </form>
              </div>
            </div>
          );
        }}
      </Consumer>
    );
  }
}

export default AddContacts;

Here is the App.js file

import React, { Component } from "react";
import { BrowserRouter, Routes, Route, Link } from "react-router-dom";

import Contacts from "./components/contacts/Contacts";
import Header from "./components/layout/Header";
import AddContacts from "./components/contacts/AddContacts";
import About from "./components/pages/About";

import { Provider } from "./context";

import "bootstrap/dist/css/bootstrap.min.css";
import "./App.css";

function App() {
  return (
    <Provider>
      <BrowserRouter>
        <div className="App">
          <Header branding="Contact manager" />
          <div className="container">
            <Routes>
              <Route path="/" element={<Contacts />} />{" "}
              <Route path="/contact/add/*" element={<AddContacts />} />{" "}
              <Route path="about/*" element={<About />} />{" "}
            </Routes>{" "}
          </div>{" "}
        </div>{" "}
      </BrowserRouter>{" "}
    </Provider>
  );
}

export default App;

Issue

TypeError: Cannot read properties of undefined (reading 'push')

This is cause by you attempting to navigate from a navigate prop that doesn't exist, it's undefined.

this.props.navigate.push("/");

The useNavigate hook is only compatible with function components, so of you want/need to use navigate with a class component you must either convert AddContacts to a function component, or roll your own custom withRouter Higher Order Component to inject the "route props" like the withRouter HOC from react-router-dom v5.x did.

Solution

I won't cover converting a class component to function component. Here's an example custom withRouter HOC:

const withRouter = WrappedComponent => props => {
  const navigate = useNavigate();
  // etc... other react-router-dom v6 hooks

  return (
    <WrappedComponent
      {...props}
      navigate={navigate}
      // etc...
    />
  );
};

And decorate the AddContacts component with the new HOC.

export default withRouter(AddContacts);

This will now pass a navigate prop (and any others you set up) to the decorated components and this.navigate will now be defined.

Additionally, the navigation API changed from v5 to v6, it's no longer the direct history object being used. navigate is a function instead of an object. To use you invoke the function and pass 1 or 2 arguments, the first is the target path, the second is an optional "options" object with replace and/or state key/values.

interface NavigateFunction {
  (
    to: To,
    options?: { replace?: boolean; state?: State }
  ): void;
  (delta: number): void;
}

To navigate now as follows:

this.props.navigate("/");