ReactJS - Redirect in main component - javascript

I want to redirect to other page with search results after submitting form (search bar feature).
I will usually redirect with return <Redirect to={'/search/' + this.state.value} /> in render(), but I can't now since I need to render entire site (search bar is in main component).
I already tried this.props.history.push('/search/' + this.state.value) and this.context.router.history.push('/search/' + this.state.value) in place of comment but it doesn't work.
(Code simplified for clarity)
import React, { Component } from "react";
import {
Route,
NavLink,
BrowserRouter
} from "react-router-dom";
import Search from "./Search";
import Autosuggest from 'react-autosuggest';
class App extends Component {
constructor(props) {
super(props);
this.state = { value: '' };
this.search = this.search.bind(this);
}
// react-autosuggest code
search(e) {
e.preventDefault();
// Redirect to '/search/' + this.state.value
}
render() {
return (
<BrowserRouter>
<div className="app">
<div className="header">
<form className="search-form" onSubmit={this.search}>
{/* Autosuggest input which updates this.state.value */}
</form>
</div>
<div className="main">
<Route path="/search/:handle" component={Search} />
{/* Other Routes */}
</div>
</div>
</BrowserRouter>
);
}
}
export default App;
Both this.props.history and this.context.router.history are undefined if I try to use them. <Redirect /> (obviously) can't be rendered instead of <BrowserRouter />

Have you tried using withRouter HOC to App, giving you access to history as a prop?
class App extends Component {
// ...
search(e) {
e.preventDefault();
this.props.history.push(`/search/${this.state.value}`)
}
// ....
}
export default withRouter(App)

You need to import Redirect from react-router-dom,
import {Redirect} frpm 'react-router-dom'
And usage,
search(e) {
e.preventDefault();
if(this.state.value){
return <Redirect to={`/search/${this.state.value}`}
}
}

Related

Redirecting to another page(route) from a React class component

I need some help to solve the following issue with using React.
In some web app I have a landing page, where I want to redirect the user to the login page in case she or he is not logged in.
I want to use the following landing page (taken from some tutorial I found on the net) in order to use it as a model for mine.
The problem is that this is a function component while my landing page is a class component. According to what I understand I guess I need to consider the code inside useEffect and (somewhat) transfer it to componentDidMount() in my class component. But I don't know how to do that. history.replace will not work in a class component (no Hooks in Classes). Any advice from a more React experienced user will be very welcome.
import React, { useEffect, useState } from "react";
import { useAuthState } from "react-firebase-hooks/auth";
import { useHistory } from "react-router";
import "./Dashboard.css";
import { auth, db, logout } from "./firebase";
....
function Dashboard() {
const [user, loading, error] = useAuthState(auth);
const [name, setName] = useState("");
const history = useHistory();
....
useEffect(() => { // Important part for my question !
if (loading) return;
if (!user) return history.replace("/");
....
}, [user, loading]);
return (
<div>
{/*...*/}
<button className="dashboard__btn" onClick={logout}>
Logout
</button>
</div>
);
}
export default Dashboard;
Here is what I tried on my Class Component:
class MyCompo extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
console.log("--componentDidMount(MyCompo)--");
const { history } = this.props
history.push("/login");
}
.....
}
But I get the following error:
TypeError: history is undefined
componentDidMount
=============== Added information ===============
Below is the relevant part of the code I have been working on:
This part is what works:
<Route exact path="/" component={TopMenu}>
{true && <Redirect to="/login" />}
</Route>
What I tried in the Links Component did not work.
The code:
....
ReactDOM.render(
<Router>
<Switch>
<Route exact path="/" component={TopMenu}>
{true && <Redirect to="/login" />}
</Route>
<Route exact path="/login" component={Login} />
<Route exact path="/section1" component={Section1Page}/>
<Route exact path="/section2" component={Section2Page}/>
<Route exact path="/section3" component={Section3Page}/>
</Switch>
</Router>,
document.getElementById('root')
);
....
const TopMenu = () => {
return (
<div className='page_container'>
<Title/>
<Links path='/'/>
<button className="dashboard__btn" onClick={logout}>
Logout
</button>
</div>
)
};
class Links extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
console.log("--componentDidMount(Links)--");
// This is some code I tried with no success.
const { history } = this.props
//history.push("/login");
}
componentDidUpdate(prevProps, prevState) {
console.log("--componentDidUpdate(Links)--");
}
render() {
return (
<div className='links_container'>
{(this.props.path != '/mng') &&
<React.StrictMode>
<Link to='/mng'>{mnMgrStr()}</Link><br/>
</React.StrictMode>}
{(this.props.path != '/other') &&
<React.StrictMode>
<Link to='/other'>{otherInpStr()}</Link><br/>
</React.StrictMode>}
.......
</div>
)
}
}
Following the example on the React Router docs you can use withRouter if your component isn't already receiving the route props, otherwise you can access history from the props.
class MyComponent extends React.Component {
...
componentDidMount() {
const { history } = this.props
// do whatever with history here
}
...
}
In react-router-dom version 5 there are a couple ways a class component can access the history object.
Rendered directly by a Route component via the component, or render or children function props so route props (i.e. history, location, and match) are passed.
component: <Route path="....." component={MyCompo} />
render: <Route path="....." render={routeProps => <MyCompo {...routeProps} />} />
Access the history object from the passed route props:
class MyCompo extends React.Component {
componentDidMount() {
const { history } = this.props;
history.push("/login");
}
...
}
Decorated by the withRouter Higher Order Component so the route props are injected.
import { withRouter } from 'react-router-dom';
class MyCompo extends React.Component {
componentDidMount() {
const { history } = this.props;
history.push("/login");
}
...
}
export default withRouter(MyCompo);
Well I hope by answering this question I can save lot of time of others. Don't need to panic it's not a major issue. I will explain step by step reason and solution.
First of all why this happening is
In react-router-dom **V6 (version 6) latest ** there is no history export or redirect.
There is navigate construct.
So to achieve in functional component there is useNavigate() hook.
Now coming to answer...
To redirect in class component using react-router-dom V6 we have to use component.
So now one has to follow the following steps:
Import navigate
import { Navigate } from "react-router-dom";
Use Navigate to redirect
So above I discussed syntax to do so now coming to your exact problem
You have to redirect user to login if he is not logged in
You can follow these steps:
create state to store status of user like logged in or not (inside constructor of class)
this.state = {
userLogged: false,
};
in your render method you have to add some condition like if user is not logged in take user to login page. see below..
render() {
const { userLogged } = this.state;
if (goToPay) {
return (
<Navigate to="/cart" state={selectedTiffin} props={selectedTiffin} />
);
}
}
That's it.
It can be confusing so I am giving full example so you can save your lot of time..
import React from "react";
import { Navigate } from "react-router-dom";
class Solve extends React.Component {
constructor(props) {
super(props);
this.state = {
userLogged: false,
};
}
// here you can write code to set the status of user like logged in or not
render() {
const { userLogged } = this.state;
if (userLogged ) {
return (
<Navigate to="/cart" />
);
}
return (
<>
Here you can return your original component that should be render when user is log in
</>
);
}
}
I hope this will help and work. Thank You

React component not updating when state is changed

I am having an issue with my navigation not being updated when a user is being logged in.
So my a portion of my App.js looks like so;
import React from "react";
import { BrowserRouter as Router, Route } from "react-router-dom";
import Home from './views/Home';
import Login from './views/Login';
import Navigation from './components/Navigation';
function App() {
return (
<Router>
<React.Fragment>
<header className="header-global">
<Navigation />
</header>
<Route path="/" exact component={Home} />
<Route path="/login" component={Login} />
<Footer />
</React.Fragment>
</Router>
);
}
export default App;
In my Login.js file, I make a call to my API, check for valid details, if they are valid the response sets a token and also a expires at value into localStorage and then redirects to the homepage;
.then((response) => {
// Set token here
// set expires at here
// redirect to homepage after successful login
this.props.history.push("/");
This all works flawless, but in my Navigation.js file I want to change the link from Login to Logout but after the redirect it doesnt automatically do that .. I have to actually reload the page to see the Login change to Logout.
I have a seperate file called auth.js which has a function called isAuthenticated, this basically just checks that the token is there and valid and returns either true or false based on this.
This is my Navigation.js file (which i have cut down to only display whats needed), but i dont see anything wrong with it and I cant understand why its not automatically changing the value after the redirect.
import React, { Component } from 'react';
import auth from '../auth';
class Navigation extends Component {
constructor(props) {
super(props);
this.state = {
isAuthenticated: false,
};
}
componentDidMount() {
if ( auth.isAuthenticated() ) {
this.setState({ isAuthenticated: true })
}
}
render() {
const { isAuthenticated } = this.state;
return (
{ isAuthenticated ? 'Logout' : 'Login' }
);
}
}
export default Navigation;
I have also tried using the componentWillMount method as well.
The following is the auth.isAuthenticated code (cut down)
isAuthenticated () {
if (this.isExpired()) {
return false
}
if (!this.getToken()) {
return false
}
return true
}
You need to add props history for Navigation:
import React from "react";
import { BrowserRouter as Router, Route } from "react-router-dom";
import Home from './views/Home';
import Login from './views/Login';
import Navigation from './components/Navigation';
function App() {
return (
<Router>
<React.Fragment>
<header className="header-global">
<Navigation />
</header>
<Route path="/" exact component={Home} />
<Route path="/login" component={Login} />
<Footer />
</React.Fragment>
</Router>
);
}
export default App;
and in componentDidMount add this.props.history.push("/"):
import React, { PureComponent } from 'react';
import auth from '../auth';
class Navigation extends PureComponent {
constructor(props) {
super(props);
this.state = {
isAuthenticated: false,
};
}
componentDidMount() {
if ( auth.isAuthenticated() ) {
this.setState({ isAuthenticated: true });
this.props.history.push("/");
}
}
render() {
const { isAuthenticated } = this.state;
return (
{ isAuthenticated ? 'Logout' : 'Login' }
);
}
}
export default withRouter(Navigation);
Example:

Redirecting using React Router shows blank page

I'm trying to build a simple example project where the user is redirected to the 'contact' page upon clicking a button, using React. I'm trying to achieve this by setting the value of a state property. When I run the code I have, it does change the browser address bar URL to that of the contact page, but does not seem to actually load the component - I get a blank page instead. If I manually navigate to that URL (http://localhost:3000/contact) I can see the contents.
Here are my App.js and Contact.js files -
App.js
import React, { Component } from 'react';
import { BrowserRouter as Router, Switch, Route, Link, Redirect } from 'react-router-dom';
import Contact from './Contact';
class App extends Component {
state = {
redirect: false
}
setRedirect = () => {
this.setState({
redirect: true
})
}
renderRedirect = () => {
if (this.state.redirect) {
return <Redirect to='/contact' />
}
}
render() {
return (
<Router>
<Switch>
<Route exact path='/contact' component={Contact} />
</Switch>
<div>
{this.renderRedirect()}
<button onClick={this.setRedirect}>Redirect</button>
</div>
</Router>
)
}
}
export default App;
Contact.js
import React, { Component } from 'react';
class Contact extends Component {
render() {
return (
<div>
<h2>Contact Me</h2>
<input type="text"></input>
</div>
);
}
}
export default Contact;
Using state isn't really a requirement for me, so other (preferably simpler) methods of redirection would be appreciated too.
Since your button is nothing more than a link, you could replace it with:
<Link to="/contact">Redirect</Link>
There are many alternatives though, you could for example look into BrowserRouter's browserHistory:
import { browserHistory } from 'react-router'
browserHistory.push("/contact")
Or perhaps this.props.history.push("/contact").
There are pros and cons to every method, you'll have to look into each and see which you prefer.
I got here for a similiar situation. It's possible use withRouter (https://reactrouter.com/web/api/withRouter) to handle that.
This example was tested with "react": "^16.13.1","react-router-dom": "^5.2.0" and "history": "^5.0.0" into "dependecies" sections in package.json file.
In App.js I have the BrowserRouter (usually people import BrowserRouter as Router, I prefer work with original names) with Home and Contact.
App.js
import React, { Component } from "react";
import {
BrowserRouter,
Switch,
Route,
} from "react-router-dom";
import Home from "./pages/Home";
import Contact from "./pages/Contact";
class App extends Component
{
// stuff...
render()
{
return (
<BrowserRouter>
<Switch>
<Route path="/contact">
<Contact />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
</BrowserRouter>
);
}
}
export default App;
ASIDE 1: The Route with path="/contact" is placed before path="/" because Switch render the first match, so put Home at the end. If you have path="/something" and path="/something/:id" place the more specific route (with /:id in this case) before. (https://reactrouter.com/web/api/Switch)
ASIDE 2: I'm using class component but I believe (I didn't test it) a functional component will also work.
In Home.js and Contact.js I use withRouter associated with export keyword. This makes Home and Contact components receive the history object of BrowserRouter via props. Use method push() to add "/contact" and "/" to the history stack. (https://reactrouter.com/web/api/history).
Home.js
import React from "react";
import {
withRouter
} from "react-router-dom";
export const Home = ( props ) =>
{
return (
<div>
Home!
<button
onClick={ () => props.history.push( "/contact" ) }
>
Get in Touch
<button>
</div>
);
}
export default withRouter( Home );
Contact.js
import React from "react";
import {
withRouter
} from "react-router-dom";
export const Contact = ( props ) =>
{
return (
<div>
Contact!
<button
onClick={ () => props.history.push( "/" ) }
>
Go Home
<button>
</div>
);
}
export default withRouter( Contact );
Particularly, I'm using also in a BackButton component with goBack() to navigate backwards:
BackButton.js
import React from "react";
import {
withRouter
} from "react-router-dom";
export const BackButton = ( props ) =>
{
return (
<button
onClick={ () => props.history.goBack() }
>
Go back
<button>
);
}
export default withRouter( BackButton );
So I could modify the Contact to:
Contact.js (with BackButton)
import React from "react";
import BackButton from "../components/BackButton";
export const Contact = ( props ) =>
{
return (
<div>
Contact!
<BackButton />
</div>
);
}
export default Contact; // now I'm not using history in this file.
// the navigation responsability is inside BackButton component.
Above was the best solution for me. Other possible solutions are:
useHistory Hook (https://reactrouter.com/web/api/Hooks)
work with Router instead BrowserRouter - (https://reactrouter.com/web/api/Router)

Using react api context alongside react router

I have a problem with passing context to route. I get an error when i click a link that goes to my component where context was passed from App component. Below is that component with App (only one import just to show where Context is coming from):
App.js
import { Context } from './Context';
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
cryptolist: []
}
}
componentDidMount = () => {
fetch('https://api.coinmarketcap.com/v2/ticker/?structure=array')
.then(response => response.json())
.then(json => this.setState({
cryptolist: json.data
}))
}
render() {
return (
<div>
<Menu />
<Context.Provider value={this.state}>
<Userlist />
</Context.Provider>
</div>
)
}
}
export default App;
Userlist.js ( should be cryptolist or something )
import { Context } from '.././Context'
export default class Userlist extends Component {
render() {
return (
<main>
<Context.Consumer>
{(context) => context.cryptolist.map(el => {
return (
<div>
<h2>{el.name}</h2>
<h5>{el.symbol}</h5>
<h3>{el.quotes.USD.price}</h3>
</div>
)
})}
</Context.Consumer>
</main>
)
}
}
Context.js
import React from 'react';
export const Context = React.createContext();
Everything works just fine here untill i wanted to make a menu that links to this component.
import React from "react";
import { slide as Slider } from 'react-burger-menu';
import { BrowserRouter as Router, Route, Link, Switch} from "react-router-dom";
import Main from './main';
import Userlist from './userlist';
export default class Menu extends React.Component {
render () {
return (
<Router>
<div className="bg-navy w-100 h-100">
<Slider width={ 180 } isOpen={ false }>
<Link className="menu-item" to="/main">Home</Link>
<Link className="menu-item" to="/crypto">About</Link>
</Slider>
<Switch>
<Route path="/main" component={Main} />
<Route path="/crypto" component={Userlist} />
</Switch>
</div>
</Router>
);
}
}
When i click a link to component Userlist i get an error thats cryptolist is not defined. I get it that Userlist can't see a context after clicking link to it. How to pass it correctly?
You are using the routes in the Menu component. Is this really you want? Though, I don't know how this slide thingy works. Maybe this is the way you want to go. I think your problem occurs because your Menu component is not wrapped by the provider. Try like this:
<Context.Provider value={this.state}>
<Menu />
<Userlist />
</Context.Provider
Your Menu component will call Userlist but as it is out the Provider the context doesn’t exist!
Replace Userlist in Context.Provider by Menu and all will be fine.

Passing state value from component to component?

I've seen many answer on this website and in official docs and many other places. But I couldn't able to achieve results. That's why I'm posting this question.
SO, my question is I have 3 files named:
App.js, SignUp.js and Welcome.js.
My task is to get value in form from SignUp.js page and print that value to the Welcome.js component. I read about lifting state up and higher order components but couldn't able to do it.
Any help is appreciated.
Here's my code:
App.js:
import React,{Component} from "react";
import {BrowserRouter as Router, Link, Switch, Route} from "react-router-dom";
import SignUp from './SignUp';
import Welcome from './Welcome';
class App extends Component {
render(){
var homeMessage = () =>{
return(<div><SignUp /></div>);
}
return(
<Router>
<div>
<Route exact path="/src/Welcome" component={Welcome}/>
<Route exact path="/" component={homeMessage}/>
</div>
</Router>
);
}
}
export default App;
SignUp.js;
import React,{Component} from "react";
import {Link} from "react-router-dom";
export default class SignUp extends Component {
constructor(props){
super(props);
this.state = {
firstName:''
};
}
inputData = event =>
{
this.setState({
[event.target.name]:event.target.value
});
}
submitData = event =>
{
event.preventDefault();
}
render(){
return(
<div>
<form onSubmit={this.submitData}>
<input type="text" name="firstName" onChange={this.inputData}/>
<button type="submit"> Submit</button>
</form>
<Link to="/src/Welcome">Welcome</Link>
</div>
);
}
}
Welcome.js:
import React,{Component} from "react";
import SignUp from './SignUp';
export default class Welcome extends Component {
render(){
return(
<div>
{this.state.firstName}
</div>
);
}
}
"And please give answer if possible then in react-way only not redux or any other library. Because as per Dan Abramov article "Thinking in React" so first I want to try all scope which is available in react only."
Pass the data back to the parent from SignUp component and then pass it as a prop to the Welcome component. The other way is to store the SignUp component state in the App component so that you don't need to pass it back up when you submit.
App.js
import React,{Component} from "react";
import {BrowserRouter as Router, Link, Switch, Route} from "react-router-dom";
import SignUp from './SignUp';
import Welcome from './Welcome';
class App extends Component {
state = {
firstName: ''
}
onSignIn = (value) => {
this.setState({firstName: value});
}
render(){
return(
<Router>
<div>
<Route exact path="/src/Welcome" render={(props) => <Welcome firstName={this.state.firstName} {...props}/>}/>
<Route exact path="/" render={(props) =>{
return(<div><SignUp onSignIn={this.onSignIn} {...props} /></div>);
}}/>
</div>
</Router>
);
}
}
export default App;
SignUp.js
export default class SignUp extends Component {
constructor(props){
super(props);
this.state = {
firstName:''
};
}
inputData = event =>
{
this.setState({
[event.target.name]:event.target.value
});
}
submitData = event =>
{
event.preventDefault();
this.props.onSignIn(this.state.firstName);
}
render(){
return(
<div>
<form onSubmit={this.submitData}>
<input type="text" name="firstName" onChange={this.inputData}/>
<button type="submit"> Submit</button>
</form>
<Link to="/src/Welcome">Welcome</Link>
</div>
);
}
}
Welcome.js
import React,{Component} from "react";
import SignUp from './SignUp';
export default class Welcome extends Component {
render(){
return(
<div>
{this.props.firstName}
</div>
);
}
}
Some useful links:
How to pass data from child component to its parent in ReactJS?
Passing custom props to router component in react-router v4
ReactJS - Lifting state up vs keeping a local state

Categories

Resources