Render to another functional component based on route - javascript

I have routes.js -
import React from 'react'
import { Switch, Route , Link } from 'react-router';
import {BrowserRouter, Router} from 'react-router-dom';
import SignIn from './Components/Login/SignIn';
import MainPage from './Components/MainPage';
function Routes() {
return (
<Router>
<Switch>
<Route path="/logout" component={SignIn} ></Route>
<Route path="/home" component={MainPage} ></Route>
</Switch>
</Router>
)
}
export default Routes
Within SignIn.js I have button from which I want to render to main page.
Main bits of SignIn.Js are as below -
import MainPage from '../MainPage';
import { useHistory } from 'react-router-dom';
<FormGroup>
<Button style={{width:'100%',backgroundColor:"#FCB724",color:"black",fontWeight:"bold"}} onClick={NavigateToMainPage} >Sign in using our secure server</Button>
</FormGroup>
function NavigateToMainPage(){
let path = `/home`;
let history = useHistory();
history.push(path);
}
This is not navigating.
How can I navigate on button click to another component in this case ?

I'm not sure how this code isn't breaking completely because you can't use a hook outside of a react component, you can't use it in a normal function.
const Form = () => {
const history = useHistory()
return <form>
<button onClick={() => history.push('/home')}>Example</button>
</form>
}
You want something like that.

You have to change how you call a function inside an on click prop.
If you write onClick={functionName}, the functionName would be always executed when the component is rendered and it would produce an infinite loop.
You have to change it into this onClick={() => functionName()} means that the functionName will be executed only after the the onclick is executed.
Don't forget to add return to the component.
Also if you use hook (like something starts from use word), you have to put it inside the functional component.
Here is the full code:
import MainPage from '../MainPage';
import { useHistory } from 'react-router-dom';
const SignIn = () => {
const NavigateToMainPage = () => {
let path = `/home`;
let history = useHistory();
history.push(path);
}
return(
<FormGroup>
<Button
style={{width:'100%',backgroundColor:"#FCB724",color:"black",fontWeight:"bold"}}
onClick={() => NavigateToMainPage()}
>
Sign in using our secure server
</Button>
</FormGroup>
)
}
export default SignIn
Additional info:
I personally use const (ES6 arrow function https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) rather than function to declare a function.
In the MainPage.js, you can directly import the Route, Switch, and Link from react-router-dom.
So you can change it from this:
import {Switch, Route , Link} from 'react-router';
import {BrowserRouter, Router} from 'react-router-dom';
into this:
import {BrowserRouter, Router, Switch, Route, Link} from 'react-router-dom';

I'd implement your example using NavLink component instead of useHistory hook.
import MainPage from '../MainPage';
import { NavLink} from 'react-router-dom';
<FormGroup>
<NavLink to="/home" style={{width:'100%',backgroundColor:"#FCB724",color:"black",fontWeight:"bold"}} onClick={NavigateToMainPage}>Sign in using our secure server</NavLink>
</FormGroup>
To top it off, please use const variables, forget about var.

Related

React router - url changes, no render

Using a simple link to / route to via React browser Router.
I got it to work fine with routing to the root component (/), however it does not function as expected when trying to route to (/drink/:drinkId), though the URL changes and the page loads if I manually try to access it.
App component:
import React from "react";
import "./App.css";
import Cocktails from "./Cocktails";
import { Container } from "react-bootstrap";
import NavApp from "./Navbar";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
import DrinkDetails from "./DrinkDetails";
import "./App.css";
function App() {
return (
<Router>
<Container className="App-container">
<NavApp />
<Switch>
<Route exact path="/">
<Cocktails size={20} />
</Route>
<Route exact path="/drink/:drinkId">
<DrinkDetails />
</Route>
</Switch>
</Container>
</Router>
);
}
export default App;
Drink details component:
import { React, useState, useEffect } from "react";
import { Jumbotron, Button } from "react-bootstrap";
import {
BrowserRouter as Router,
Switch,
Route,
Link,
useParams,
} from "react-router-dom";
import axios from "axios";
function DrinkDetails() {
let { drinkId } = useParams();
const [drink, setDrink] = useState(null);
const [ingrdts, setIngrdts] = useState([]);
useEffect(() => {
const getDrinkSpecs = async () => {
const res = await axios.get(
`https://www.thecocktaildb.com/api/json/v1/1/lookup.php?i=${drinkId}`
);
let newDrink = res.data.drinks[0];
setDrink(newDrink);
let newIngrdts = [];
for (let i = 1; i <= 15; i++) {
if (newDrink[`strIngredient${i}`] != null) {
let ingrdtVal = {};
ingrdtVal["ing"] = newDrink[`strIngredient${i}`];
ingrdtVal["val"] = newDrink[`strMeasure${i}`];
newIngrdts.push(ingrdtVal);
}
}
setIngrdts([...newIngrdts]);
};
getDrinkSpecs();
}, [drinkId]);
return drink ? (
<Jumbotron>
<h1>
{drink.strDrink}
<img src={drink.strDrinkThumb} />
</h1>
<p>Glass: {drink.strGlass}</p>
<p>Category: {drink.strCategory}</p>
<p>Instructions: {drink.strInstructions}</p>
{ingrdts.map((ingrdt) => (
<p>
{ingrdt.ing} : {ingrdt.val}
</p>
))}
<p>
<Button variant="primary">Learn more</Button>
</p>
</Jumbotron>
) : (
<Jumbotron>
<h1>Hmmm... we don't have this yet!</h1>
<p>
This is a simple hero unit, a simple jumbotron-style component for
calling extra attention to featured content or information.
</p>
<p>
<Button variant="primary">Learn more</Button>
</p>
</Jumbotron>
);
}
export default DrinkDetails;
and this is where I use Link:
import React from "react";
import { Button, Card } from "react-bootstrap";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
import "./Card.css";
function CardDrink({ data, select }) {
return (
<Router>
<Card className="Cocktail-card">
<Link to={`/drink/${data.idDrink}`}>
<Card.Img
variant="top"
src={data.strDrinkThumb}
className="Cocktail-card-img"
onClick={() => select(data.idDrink)}
/>
</Link>
<Card.Body>
<Card.Title>{data.strDrink}</Card.Title>
</Card.Body>
</Card>
</Router>
);
}
export default CardDrink;
Remove <Router> from CardDrink component. You need only one Router at root level which you already have in App component.
Also, as a practice don't keep unused imports in your component. I see Router imported in DrinkDetails component as well.
From docs
To use a router, just make sure it is rendered at the root of your element hierarchy. Typically you’ll wrap your top-level element in a router.
Try to use the useHistory() hook:
import React from "react";
import { Button, Card } from "react-bootstrap";
import { BrowserRouter as Router, Switch, Route, Link, useHistory } from "react-router-dom";
import "./Card.css";
function CardDrink({ data, select }) {
const history = useHistory()
return (
<Router>
<Card className="Cocktail-card">
<div onClick={()=>history.push(`/drink/${data.idDrink}`)}>
<Card.Img
variant="top"
src={data.strDrinkThumb}
className="Cocktail-card-img"
onClick={() => select(data.idDrink)}
/>
</div>
<Card.Body>
<Card.Title>{data.strDrink}</Card.Title>
</Card.Body>
</Card>
</Router>
);
}
export default CardDrink;

How to redirect in React on click?

In my react app, I am using the useHistory hook to redirect to my Home Component. Why is useHistory undefined, and how would I solve this problem?
App.js
import 'bootstrap/dist/css/bootstrap.css'
import React from "react";
import { Button } from 'react-bootstrap';
import { BrowserRouter, Route, useHistory} from 'react-router-dom';
import { Switch } from 'react-router-dom/cjs/react-router-dom.min';
import './App.css';
import Home from './components/Home/Home';
const App = () =>
{
const history = useHistory()
const handleClick = () =>
{
console.log(history)
console.log("handle click")
// history.push("/home") this line throws "cannot read property of undefined" error
}
return (
<>
<BrowserRouter>
<Button onClick = {handleClick} variant = "primary">Get Started</Button>
<Switch>
<Route path="/home" exact component = {Home}/>
</Switch>
</BrowserRouter>
</>
);
}
export default App;
Try to use the useHistory() outside this component and see it work. It seems you are calling the function before the Router itself and both are imported from react-router-dom .

ReactJS : Nested navigations not works, not reaching the expected page

Using React ^16.13.1 and react-router-dom ^5.2.0, We have multiple Navigation files to make nested navigation, the first Navigation.js runs and redirects fine, but the second Navigation.js does not work as we expected.
Created a react APP using npx create-react-app nested
Listing the important files for review:
App.js
import React from 'react';
import logo from './logo.svg';
import './App.css';
import Navigation from "./Navigation";
import { BrowserRouter } from "react-router-dom";
const App = () => {
return (
<BrowserRouter>
<Navigation />
</BrowserRouter>
);
};
export default App;
Navigation.js
import React from "react";
import { Switch, Route, BrowserRouter } from "react-router-dom";
import nestedNavigation from "./nested/Navigation";
const NotFound = () => <h1>Not Found</h1>;
const Navigation = () => {
return (
<Switch>
<Route exact path="/welcome" component={nestedNavigation} />
<Route path="/" component={NotFound} />
</Switch>
);
};
export default Navigation;
nested/Navigation.js nested navigation - the second one
import React from "react";
import {
Switch,
Route,
BrowserRouter,
useRouteMatch,
} from "react-router-dom";
import Welcome from "../Welcome"
const Navigation = () => {
let { path, url } = useRouteMatch();
debugger;
return (
<Switch>
<Route path={`${path}/nested`} exact component={Welcome} />
</Switch>
);
}
export default Navigation;
Nested routes require the full path in the most recent full release version of React Router, add the rest of the URL from the upper components to the path prop. codesandbox from react-router Docs
Also remove the exact from your welcome. Sub-routes wont likely work with exact because they aren’t exactly that route!

react-router-dom gives me 404

I started adding routes to my app, the problem is that even after I render them I cannot access them (get a 404). I tried this, this and this, but I cannot make it work. This is my index
import React from 'react'
import ReactDOM from 'react-dom';
import App from './containers/App';
import { BrowserRouter } from 'react-router-dom';
import './index.css';
const app = (
<BrowserRouter>
<App />
</BrowserRouter>
);
ReactDOM.render(app, document.getElementById('root'));
and my App.jsx is just
import React, { Component } from 'react';
import {Route, Switch} from 'react-router-dom';
class App extends Component {
render() {
return (
<div>
<Switch>
<Route exact path = '/foo' render = {() => <h1>foo</h1>} />
<Route exact path = '/' render = {() => <h1>Home</h1>} />
</Switch>
<a href = '/foo'>Go to foo</a>
</div>
)
}
}
export default App;
In theory, if I click on the anchor tag, I should redirected to the foo path, but instead I get a 404. Any suggestion is greatly appreciated
You can use the Link component of react-router-dom:
import { Link } from 'react-router-dom'
<Link to="/foo">Go to foo</Link>
This will generate an <a> tag but will respect the client routing
Here is the solution!
Remove exact from exact path = '/foo'
With a Tag, page will get loaded, hence better to use <Link to='/foo'>roster</Link>
Here is the example which you can refer
You can import {Link} from react-router it gives an a tag and you can redirect

react-router (v4) how to go back?

Trying to figure out how can I go back to the previous page. I am using [react-router-v4][1]
This is the code I have configured in my first landing page:
<Router>
<div>
<Link to="/"><div className="routerStyle"><Glyphicon glyph="home" /></div></Link>
<Route exact path="/" component={Page1}/>
<Route path="/Page2" component={Page2}/>
<Route path="/Page3" component={Page3}/>
</div>
</Router>
In order to forward to subsequent pages, I simply do:
this.props.history.push('/Page2');
However, how can I go back to previous page?
Tried few things like mentioned below but no luck:
1. this.props.history.goBack();
Gives error:
TypeError: null is not an object (evaluating 'this.props')
this.context.router.goBack();
Gives error:
TypeError: null is not an object (evaluating 'this.context')
this.props.history.push('/');
Gives error:
TypeError: null is not an object (evaluating 'this.props')
Posting the Page1 code here below:
import React, {Component} from 'react';
import {Button} from 'react-bootstrap';
class Page1 extends Component {
constructor(props) {
super(props);
this.handleNext = this.handleNext.bind(this);
}
handleNext() {
this.props.history.push('/page2');
}
handleBack() {
this.props.history.push('/');
}
/*
* Main render method of this class
*/
render() {
return (
<div>
{/* some component code */}
<div className="navigationButtonsLeft">
<Button onClick={this.handleBack} bsStyle="success">< Back</Button>
</div>
<div className="navigationButtonsRight">
<Button onClick={this.handleNext} bsStyle="success">Next ></Button>
</div>
</div>
);
}
export default Page1;
I think the issue is with binding:
constructor(props){
super(props);
this.goBack = this.goBack.bind(this); // i think you are missing this
}
goBack(){
this.props.history.goBack();
}
.....
<button onClick={this.goBack}>Go Back</button>
As I have assumed before you posted the code:
constructor(props) {
super(props);
this.handleNext = this.handleNext.bind(this);
this.handleBack = this.handleBack.bind(this); // you are missing this line
}
UPDATED:
Now we have hook, so we can do it easily by using useHistory
const history = useHistory()
const goBack = () => {
history.goBack()
}
return (
<button type="button" onClick={goBack}>
Go back
</button>
);
ORIGINAL POST:
this.props.history.goBack();
This is the correct solution for react-router v4
But one thing you should keep in mind is that you need to make sure this.props.history is existed.
That means you need to call this function this.props.history.goBack(); inside the component that is wrapped by < Route/>
If you call this function in a component that deeper in the component tree, it will not work.
EDIT:
If you want to have history object in the component that is deeper in the component tree (which is not wrapped by < Route>), you can do something like this:
...
import {withRouter} from 'react-router-dom';
class Demo extends Component {
...
// Inside this you can use this.props.history.goBack();
}
export default withRouter(Demo);
For use with React Router v4 and a functional component anywhere in the dom-tree.
import React from 'react';
import { withRouter } from 'react-router-dom';
const GoBack = ({ history }) => <img src="./images/back.png" onClick={() => history.goBack()} alt="Go back" />;
export default withRouter(GoBack);
Each answer here has parts of the total solution. Here's the complete solution that I used to get it to work inside of components deeper than where Route was used:
import React, { Component } from 'react'
import { withRouter } from 'react-router-dom'
^ You need that second line to import function and to export component at bottom of page.
render() {
return (
...
<div onClick={() => this.props.history.goBack()}>GO BACK</div>
)
}
^ Required the arrow function vs simply onClick={this.props.history.goBack()}
export default withRouter(MyPage)
^ wrap your component's name with 'withRouter()'
Here is the cleanest and simplest way you can handle this problem, which also nullifies the probable pitfalls of the this keyword. Use functional components:
import { withRouter } from "react-router-dom";
wrap your component or better App.js with the withRouter() HOC this makes history to be available "app-wide". wrapping your component only makes history available for that specific component``` your choice.
So you have:
export default withRouter(App);
In a Redux environment export default withRouter( connect(mapStateToProps, { <!-- your action creators -->})(App), ); you should even be able to user history from your action creators this way.
in your component do the following:
import {useHistory} from "react-router-dom";
const history = useHistory(); // do this inside the component
goBack = () => history.goBack();
<btn btn-sm btn-primary onclick={goBack}>Go Back</btn>
export default DemoComponent;
Gottcha useHistory is only exported from the latest v5.1 react-router-dom so be sure to update the package. However, you should not have to worry.
about the many snags of the this keyword.
You can also make this a reusable component to use across your app.
function BackButton({ children }) {
let history = useHistory()
return (
<button type="button" onClick={() => history.goBack()}>
{children}
</button>
)
}```
Cheers.
Can you provide the code where you use this.props.history.push('/Page2');?
Have you tried the goBack() method?
this.props.history.goBack();
It's listed here https://reacttraining.com/react-router/web/api/history
With a live example here https://reacttraining.com/react-router/web/example/modal-gallery
If using react hooks just do:
import { useHistory } from "react-router-dom";
const history = useHistory();
history.go(-1);
UPDATE 2022 w V6
navigate(-1)
to omit the current page from history:
navigate(-1, { replace: true })
Try:
this.props.router.goBack()
Simply use
<span onClick={() => this.props.history.goBack()}>Back</span>
Hope this will help someone:
import React from 'react';
import * as History from 'history';
import { withRouter } from 'react-router-dom';
interface Props {
history: History;
}
#withRouter
export default class YourComponent extends React.PureComponent<Props> {
private onBackClick = (event: React.MouseEvent): void => {
const { history } = this.props;
history.goBack();
};
...
Maybe this can help someone.
I was using history.replace() to redirect, so when i tried to use history.goBack(), i was send to the previous page before the page i was working with.
So i changed the method history.replace() to history.push() so the history could be saved and i would be able to go back.
I am not sure if anyone else ran into this problem or may need to see this. But I spent about 3 hours trying to solve this issue:
I wanted to implement a simple goBack() on the click of a button. I thought I was off to a good start because my App.js was already wrapped in the Router and I was importing { BrowserRouter as Router } from 'react-router-dom'; ... Since the Router element allows me to assess the history object.
ex:
import React from 'react';
import './App.css';
import Splash from './components/Splash';
import Header from './components/Header.js';
import Footer from './components/Footer';
import Info from './components/Info';
import Timer from './components/Timer';
import Options from './components/Options';
import { BrowserRouter as Router, Route } from 'react-router-dom';
function App() {
return (
<Router>
<Header />
<Route path='/' component={Splash} exact />
<Route path='/home' component={Info} exact />
<Route path='/timer' component={Timer} exact />
<Route path='/options' component={Options} exact />
<Footer />
</Router>
);
}
export default App;
BUT the trouble was on my Nav (a child component) module,
I had to 'import { withRouter } from 'react-router-dom';'
and then force an export with:
export default withRouter(Nav);
ex:
import React from 'react';
import { withRouter } from 'react-router-dom';
class Nav extends React.Component {
render() {
return (
<div>
<label htmlFor='back'></label>
<button id='back' onClick={ () => this.props.history.goBack() }>Back</button>
<label htmlFor='logOut'></label>
<button id='logOut' ><a href='./'>Log-Out</a>
</button>
</div>
);
}
}
export default withRouter(Nav);
in summary, withRouter was created because of a known issue in React where in certain scenarios when inheritance from a router is refused, a forced export is necessary.
You can use history.goBack() in functional component. Just like this.
import { useHistory } from 'react-router';
const component = () => {
const history = useHistory();
return (
<button onClick={() => history.goBack()}>Previous</button>
)
}

Categories