Cannot read properties of undefined (reading 'navigate') - reactjs

I am now working on routing after login in successfully. But got this error:
Uncaught TypeError: Cannot read properties of undefined (reading 'navigate')
Here is the login page
class Login extends React.Component {
constructor(props) {
super(props);
this.state = {
loginName: "",
password: "",
loginNameError: null,
passwordError: null,
};
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(event) {
event.preventDefault();
this.props.navigation.navigate("/employee")
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<div className="input-container">
<label>User ID</label>
<input type="text" name="loginName" value={this.state.loginName} onChange={(e) => (this.setState({ loginName: e.target.value }))} />
<p>{this.state.loginNameError}</p>
</div>
<div className="input-container">
<label>Password</label>
<input type="password" name="password" value={this.state.password} onChange={(e) => (this.setState({ password: e.target.value }))} />
<p>{this.state.passwordError}</p>
</div>
<div className="button-container"><input type="submit" value="CONNECT"></input></div>
</form>
);
}
}
export default Login;
Here is the APP.js
function App() {
return (
<div class="page">
<BrowserRouter>
<Routes>
<Route path="/employee" element={<UserManagement />}></Route>
<Route path="/login" element={<Login />}></Route>
</Routes>
</BrowserRouter>
</div>
);
}
export default App;
how to solve this error?

Since react router v6 doesnt have withRouter
// https://reactrouter.com/docs/en/v6/getting-started/faq#what-happened-to-withrouter-i-need-it
import {
useLocation,
useNavigate,
useParams,
} from "react-router-dom";
function withRouter(Component) {
function ComponentWithRouterProp(props) {
let location = useLocation();
let navigate = useNavigate();
let params = useParams();
return (
<Component
{...props}
router={{ location, navigate, params }}
/>
);
}
return ComponentWithRouterProp;
}
// then in the Login class component you can consume withRouter
handleSubmit(event) {
event.preventDefault();
// consume `router` prop
this.props.router.navigate("/employee");
}
// Wrap Login in withRouter HOC to make sure `router` prop is available
export default withRouter(Login);
// export default Login;
This said, I would recommend using react-router with a function component, not a class component.

Because using of class component you can not use hooks!
but I think this work here:
this.props.history.push("/employee");

Related

React V6 Not able to redirect to another page with button click with class component [duplicate]

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("/");

Going to another page in ReactJS

I am attempting to learn reactjs and I've looked up so many different resources from using react-route to react-route-dom but everything I've tried doesn't work as everything says it does so I am not sure what I am misssing.
I have a class component called LoginForm which renders the form and handles all of the logic for submitting to API and handling the response.
The api request is working and I successfully check that the login is valid, I then want to redirect to another page called dashboard.html.
Below is my component class
import React from 'react'
import * as api from '../JSFuncs/APIManager'
import 'react-router-dom'
class LoginForm extends React.Component {
constructor(props) {
super(props);
this.state = {
username: '',
password: '',
show_notice: false,
error_msg: ''
};
this.handleUsernameChange = this.handleUsernameChange.bind(this);
this.handlePasswordChange = this.handlePasswordChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleUsernameChange(event) {
this.state.username = event.target.value;
this.setState(this.state);
}
handlePasswordChange(event) {
this.state.password = event.target.value;
this.setState(this.state);
}
handleSubmit(event) {
event.preventDefault();
this.props.history.push("dashboard.html");
this.state.show_notice = true;
this.setState(this.state);
const postArray = {
username: this.state.username,
password: this.state.password
};
let comp = this;
api.sendRequest(postArray, "admin/authenticator.php", "submitLogin").then(function(result){
alert(JSON.stringify(result));
if (result.result === 0)
{
if (result.data === 0) //Login OK
{
comp.history.pushState(null, 'dashboard.html');
//comp.props.history.push('/dashboard.html');
}
comp.setState(comp.state);
}
else
{
comp.state.password = '';
comp.state.error_msg = 'An error occurred with the DB';
comp.setState(comp.state);
}
comp.state.show_notice = true;
comp.setState(comp.state);
})
}
render() {
const style = this.state.show_notice === false ? {display: 'none'} : {};
const { location, history } = this.props
return (
<section className="h-100">
<div className="container h-100">
<div className="d-flex align-items-center justify-content-center h-100">
<div className="d-flex flex-column align-self-center">
<LoginStatus style={style} error_msg={this.state.error_msg} />
<form onSubmit={this.handleSubmit} className='form-horizontal align-self-center'>
<div className='form-group row'>
<label htmlFor='txtUsername' className='col-sm-2 col-form-label'>Username: </label>
<div className='col-sm-9'>
<input type='text' className='form-control' id='txtUsername' value={this.state.username}
placeholder='Your Username' onChange={this.handleUsernameChange}/>
</div>
</div>
<div className='form-group row'>
<label htmlFor='txtPassword' className='col-sm-2 col-form-label'>Password: </label>
<div className='col-sm-9'>
<input type='password' className='form-control' id='txtPassword' value={this.state.password}
placeholder='Your password' onChange={this.handlePasswordChange}/>
</div>
</div>
<div className='formButtonContainer'>
<button className='btn-primary'>Login</button>
</div>
</form>
</div>
</div>
</div>
</section>
);
}
}
class LoginStatus extends React.Component
{
render(){
const className = this.props.error_msg === '' ? 'alert-info' : 'alert-warning';
const msg = this.props.error_msg === '' ? 'You\'ve successfully logged in' : this.props.error_msg;
return(
<div style={this.props.style} className={'alert ' + className}>
{msg}
</div>
)
}
}
export default LoginForm
In the response of handleSubmit I check if the login result is 0 and then I am using comp.history.pushState (comp is declared to be this so its in scope of the promise).
I've tried pushState and push from other examples, but I get the same type of error. I've also tried comp.state.history.push but no luck. I login successfully and show the alert box when I do the history push I get the following:
TypeError: Cannot read property 'push' of undefined
I'm very new to react so apologise if this is a simple answer but I can't seem to get my hand around how this works from everything I've googled.
In your App.js you should manage pages with routes:
import {BrowserRouter, Route, Switch} from "react-router-dom";
import { createBrowserHistory } from "history";
import {LoginForm} from './components/LoginForm';
import {Dashboard} from './components/Dashboard';
const history = createBrowserHistory();
class App extends Component {
constructor(props) {
super(props);
console.log(props)
}
render() {
path="/" is your home or main page
path="/dashboard" is your dashboard
return (
<BrowserRouter>
<div>
<Switch>
<Route path="/" render={(props) => <LoginForm props={history} {...props} /> } exact />
<Route path="/dashboard" render={(props) => <Dashboard props={history} {...props} /> }/>
</Switch>
</div>
</BrowserRouter>
)
then in your LoginForm you can
export class LoginForm extends Component {
constructor(props) {
super(props);
console.log(props)
}
go_dashboard = (e) => {
this.props.history.push("/dashboard");
}
This will then Switch to your Dashboard component.

problem with router and privaterouter / history

Hello I have a problem redirecting to a page doing a verification on a privaterouter
Unhandled Rejection (TypeError): Cannot read property 'push' of
undefined
on this line:
this.props.history.push ("/ home");
my component:
import React, { Component } from 'react';
import api from '../services/api';
import { withRouter } from 'react-router';
class LoginForm extends Component {
constructor(props){
super(props);
this.state = {
login:'',
password:'',
};
this.onSubmit = this.onSubmit.bind(this);
this.onChange = this.onChange.bind(this);
}
async onSubmit(e){
e.preventDefault();
const {login, password } = this.state;
const response = await api.post('/login', { login,password });
const user = response.data.user.login;
const {jwt} = response.data;
localStorage.setItem('token', jwt);
localStorage.setItem('user', user);
this.props.history.push("/home");
}
onChange(e){
this.setState({[e.target.name]: e.target.value});
}
render() {
const { errors, login, password, isLoading } = this.state;
return (
<form onSubmit={this.onSubmit}>
<label htmlFor="login">Login</label>
<input type="text" name="login" id="login" value={login} onChange={(e) => this.onChange(e)} placeholder="Informe seu login" />
<label htmlFor="password">Senha</label>
<input type="password" name="password" id="password" value={password} onChange={(e) => this.onChange(e)} placeholder="Informe sua senha"/>
<button className="btnEnt" type="submit">Entrar</button>
</form>
)
}
}
export default withRouter (LoginForm);
my router:
import React from 'react';
import { BrowserRouter, Switch, Route } from 'react-router-dom';
import Login from './pages/login/index';
import DashBoard from './pages/dashboard/index';
import PrivateRoute from './auth';
export default function Routes(){
return(
<BrowserRouter>
<div>
<Switch>
<Route path="/" exact component = {Login}/>
<PrivateRoute path="/home" component = {DashBoard}/>
</Switch>
</div>
</BrowserRouter>
);
}
my private route or auth router:
import React from 'react';
import { Route, Redirect} from 'react-router-dom';
const isAuth = () => {
console.log('a');
if(localStorage.getItem('token') !== null) {
console.log('true')
return true;
}
return false;
};
const PrivateRoute = ({component: Component, ...rest}) => {
return (
<Route
{...rest}
render={props =>
isAuth() ? (
<Component {...props} />
): (
<Redirect
to={{
pathname: '/',
state: {message: 'Usuário não autorizado'}
}}
/>
)}
/>
);
}
export default PrivateRoute;
I basically have my router and I also check if the user is allowed to enter this page, but I'm having trouble making it work.
Well, I read your code and here is my answer
You just need import withRouter from react-router-dom and not from react-router ;)
import { withRouter } from "react-router-dom";
And use it like
export default withRouter(LoginForm);

React-Router How to push to next page after checks

In my code I have a few checks after a user has entered some data, then I want to load the next route if everything is correct, what is the best way to do so?
This is my current Route page:
<Router history = {browserHistory}>
<Route exact path="/" component={() => <MainMenu userData={this.state.userData}/>}/>
<Route exact path="/login" component = {Login} />
<Route exact path="/pastMeetingsPlay/:meetingCode" component={(props) => <PastMeetingsPlay user={this.state.userData.UserID} {...props}/>} />
<Route exact path="/meetingMode/:meetingCode" component={(props) => <MeetingMode user={this.state.userData.UserID} {...props}/>} />
</Router>
the user submits a form then there inputs are checked and if all the required checks pass then it should load meetingMode page
EDIT:
import React, { Component } from 'react';
import './App.css';
import MeetingMode from'./MeetingMode';
import NavbarMenu from './Navbar';
import Popup from "reactjs-popup";
import axios from 'axios';
import {withRouter, history, Redirect, Route} from "react-router";
class MeetingModeLoad extends Component{
constructor(props)
{
super(props);
this.state ={
meeting:{},
value:0
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
}
async handleSubmit(event)
{
event.preventDefault();
let meetingLoadCode = this.state.value
try{
let getter = await axios.get(`https://smartnote1.azurewebsites.net/api/meetings/${meetingLoadCode}`)
let meetingLocal = getter.data
this.setState({meeting:meetingLocal})
if(meetingLocal.Status == 2)
{
console.log("please join meeting that is planned or under going")
}
else
{
console.log("/meetingMode/" + this.state.meeting.MeetingID);
this.props.history.push("/meetingMode/" + this.state.meeting.MeetingID)
}
}
catch(error)
{
console.error(error)
}
}
handleChange(event)
{
this.state.value = event.target.value
console.log(this.state.value)
}
render()
{
return(
<div>
<Popup
trigger={<button className="meetingModeButton" onClick={() => this.handleClick}>Meeting Mode</button>}
modal
closeOnDocumentClick>
<div className="newNote">
<header style={{background: "#F7941D" }}> Meeting Mode</header>
<form onSubmit={this.handleSubmit}>
<label> Enter Meeting Code :
<input type="text" name="type" className="inputBox" onChange={this.handleChange}/>
</label>
<input type="submit" value="Submit" />
</form>
</div>
{console.log(this.state.meeting)}
</Popup>
</div>
)
}
}
export default withRouter (MeetingModeLoad)
Looks like you forgot to wrap your component into withRouter. It is mandatory to access the history prop
Place this in the component from which you try to push:
import { withRouter } from 'react-router'
...
export default withRouter(YourComponent);
And push by using this in your component:
this.props.history.push("/meetingMode/" + meetingCode);

Can't call custom return component and state updating methods in react?

I've subcomponent named homeMessage which contains the form and with one input tag.
import React,{Component} from "react";
import {BrowserRouter as Router, Link, Switch, Route} from "react-router-dom";
import SignUp from './SignUp';
class App extends Component {
constructor(props){
super(props);
this.state = {
firstName: ''
};
}
inputData = (event) =>
{
this.setState({
[event.target.name]:event.target.value
});
}
submitData = (event) =>
{
event.preventDefault();
}
render(){
let homeMessage = () =>
{
return(
<div>
<form onSubmit={this.submitData}>
FirstName:
<input type="text" name="firstName" onChange={this.inputData}/>
<button type="submit">Submit</button>
</form>
<Link to="/src/SignUp">SignUp</Link>
</div>
);
}
return(
<Router>
<div>
<Route exact path="/" component={homeMessage}/>
<Route path="/src/SignUp" render={(props)=><SignUp firstName={this.state.firstName}/>}/>
</div>
</Router>
);
}
}
export default App;
When I write content of homeMessage contents in original return method it works fine but when I create custom return component like above on every keystroke it removes my cursor!
Change the homeMessage sub component's definition to this -
const homeMessage = (
<div>
<form onSubmit={this.submitData}>
FirstName:
<input type="text" name="firstName" onChange={this.inputData}/>
<button type="submit">Submit</button>
</form>
<Link to="/src/SignUp">SignUp</Link>
</div>
);
This should work fine.

Resources