React History.Push() Not Rendering New Component

Component is not getting rendered after history.push()

You're creating new history each time you invoke createHistory(). If you're using react-router-dom you can simply use the withRouter HOC that'll supply the history object to the component via a prop. You'll then utilize history.push('/'), or if it's in a class component, this.props.history.push('/') and so on.

Working example: https://codesandbox.io/s/p694ln9j0

routes (define history within routes)

import React from "react";
import { Router, Route, Switch } from "react-router-dom";
import { createBrowserHistory } from "history";
import Header from "../components/Header";
import Home from "../components/Home";
import About from "../components/About";
import Contact from "../components/Contact";
import ScrollIntoView from "../components/ScrollIntoView";

const history = createBrowserHistory();

export default () => (
<Router history={history}>
<div>
<ScrollIntoView>
<Header />
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/contact" component={Contact} />
</Switch>
<Header />
</ScrollIntoView>
</div>
</Router>
);

components/Header.js (we want to access history, however, sometimes we have to use withRouter because components like Header don't reside within a <Route "/example" component={Header}/> HOC, so it's unaware of our routing)

import React from "react";
import { withRouter } from "react-router-dom";

const Header = ({ history }) => (
<header>
<nav style={{ textAlign: "center" }}>
<ul style={{ listStyleType: "none" }}>
<li style={{ display: "inline", marginRight: 20 }}>
<button onClick={() => history.push("/")}>Home</button>
</li>
<li style={{ display: "inline", marginRight: 20 }}>
<button onClick={() => history.push("/about")}>About</button>
</li>
<li style={{ display: "inline", marginRight: 20 }}>
<button onClick={() => history.push("/contact")}>Contact</button>
</li>
</ul>
</nav>
</header>
);

export default withRouter(Header);

components/Home.js (a component like Home is aware of routing and has the history object already passed in via the <Route path="/" component={Home} /> HOC, so withRouter isn't required)

import React from "react";

const Home = ({ history }) => (
<div className="container">
<button
className="uk-button uk-button-danger"
onClick={() => history.push("/notfound")}
>
Not Found
</button>
<p>
...
</p>
</div>
);

export default Home;

Same concept as above, however, if you don't want to use withRouter, then you can simply create a history instance that'll be shared across your components that need it. You'll import this history instance and navigate with history.push('/'); and so on.

Working example: https://codesandbox.io/s/5ymj657k1k

history (define history in its own file)

import { createBrowserHistory } from "history";

const history = createBrowserHistory();

export default history;

routes (import history into routes)

import React from "react";
import { Router, Route, Switch } from "react-router-dom";
import Header from "../components/Header";
import Home from "../components/Home";
import About from "../components/About";
import Contact from "../components/Contact";
import ScrollIntoView from "../components/ScrollIntoView";
import history from "../history";

export default () => (
<Router history={history}>
<div>
<ScrollIntoView>
<Header />
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/contact" component={Contact} />
</Switch>
<Header />
</ScrollIntoView>
</div>
</Router>
);

components/Header.js (import history into Header)

import React from "react";
import history from "../history";

const Header = () => (
<header>
<nav style={{ textAlign: "center" }}>
<ul style={{ listStyleType: "none" }}>
<li style={{ display: "inline", marginRight: 20 }}>
<button onClick={() => history.push("/")}>Home</button>
</li>
<li style={{ display: "inline", marginRight: 20 }}>
<button onClick={() => history.push("/about")}>About</button>
</li>
<li style={{ display: "inline", marginRight: 20 }}>
<button onClick={() => history.push("/contact")}>Contact</button>
</li>
</ul>
</nav>
</header>
);

export default Header;

components/Home.js (is still aware of routing and has the history object already passed in via the <Route path="/" component={Home} /> HOC, so importing history isn't required, but you can still import it if you wanted to -- just don't deconstruct history in the Home's function parameters)

import React from "react";

const Home = ({ history }) => (
<div className="container">
<button
className="uk-button uk-button-danger"
onClick={() => history.push("/notfound")}
>
Not Found
</button>
<p>
...
</p>
</div>
);

export default Home;

But you might be thinking, what about BrowserRouter? Well, BrowserRouter has it's own internal history object. We can, once again, use withRouter to access its history. In this case, we don't even need to createHistory() at all!

Working example: https://codesandbox.io/s/ko8pkzvx0o

routes

import React from "react";
import { BrowserRouter, Route, Switch } from "react-router-dom";
import Header from "../components/Header";
import Home from "../components/Home";
import About from "../components/About";
import Contact from "../components/Contact";
import Notfound from "../components/Notfound";
import ScrollIntoView from "../components/ScrollIntoView";

export default () => (
<BrowserRouter>
<div>
<ScrollIntoView>
<Header />
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/contact" component={Contact} />
<Route component={Notfound} />
</Switch>
<Header />
</ScrollIntoView>
</div>
</BrowserRouter>
);

React router dom v5 history.push does not render new page

Issue

You are submitting the form but not preventing the default form action from occurring. This reloads the page before the POST request response can be handled and the UI redirected.

Solution

Move the onClick={signIn} to the Form component and switch it to the onSubmit handler, and in the signin handler consume the onSubmit event object and prevent the default form action.

function SignIn() {
const history = useHistory();

const [userEmail, setEmail] = useState("");
const [userPassword, setPassword] = useState("");

//after onClick
const signIn = (e) => { // <-- onSubmit event object
e.preventDefault(); // <-- preventDefault
// get user input
Axios.post("http://localhost:8800/signIn", {
userEmail: userEmail,
userPassword: userPassword
}).then((res) => {
console.log(res);
history.push("/clientprofile");
});
};

return (
<Form onSubmit={signIn}> // <-- onSubmit event handler
...

<Button variant="primary" type="submit"> // <-- remove onClick handler
Submit
</Button>
</Form>
);
}

Edit react-router-dom-v5-history-push-does-not-render-new-page

React history.push() not rendering new component

If anyone is interested - this was happening because the app was rendering before the history was pushed. When I put the history push into my action but just before the result is converted into JSON, it started working since now it pushes history and only then renders the App.

export const authorization = (username, password, history) => (dispatch) =>
new Promise ((resolve, reject) => {
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: username,
password: password,
})
}).then( response => {
if (response.ok) {

//################################
//This is where I put it

history.push("/servers");

//################################

response.json().then( result => {
dispatch(logUserIn(result.token));
resolve(result);
})
} else {
let error = new Error(response.statusText)
error.response = response
dispatch(showError(error.response.statusText), () => {throw error})
reject(error);
}
});
});

history.push changes url, but component is not rendered

Issue

You are creating a custom history object that your BrowserRouter doesn't know about. The LoginForm component is manipulating one version of a history object while the router and routes are using the default history object used by the routing context. The login form is able to update the URL in the address bar but the outer routing context isn't able to see this change.

Solution

Either remove the custom history and use the one provided by BrowsserRouter or or use the lower level Router component and pass the custom history object to it to be used in the routing context.

Use standard history from routing context

Router.js

<BrowserRouter >
<LoginForm/>
<Switch>
<Route path="/dashboard" component={Dashboard}/>
</Switch>
</BrowserRouter>

LoginForm - decorate with the withRouter higher order component so it receives route props from the closest routing context.

import React, {Component} from "react";
import { withRouter } from 'react-router-dom';

class LoginForm extends Component {

handleSubmit = (event) => {
event.preventDefault()
this.props.history.push("/dashboard")
}

render() {
return (
<form name="signup_form" onSubmit={this.handleSubmit}>
<input type="submit" value="Sign Up"/>
</form>
);
}

}

export default withRouter(LoginForm);

Use custom history

import LoginForm from "./LoginForm";
import { Router, Route, Switch } from "react-router-dom";
import React, { Component } from "react";
import { createBrowserHistory } from "history"

const browserHistory = createBrowserHistory()

class MyRouter extends Component {
render() {
return (
<Router history={browserHistory} >
<LoginForm />
<Switch>
<Route path="/dashboard" component={Dashboard}/>
</Switch>
</Router>
)
}
}
export default MyRouter;

And still decorate LoginForm with the withRouter HOC so the route props are passed.

import React, {Component} from "react";
import { withRouter } from 'react-router-dom';

class LoginForm extends Component {

handleSubmit = (event) => {
event.preventDefault()
this.props.history.push("/dashboard")
}

render() {
return (
<form name="signup_form" onSubmit={this.handleSubmit}>
<input type="submit" value="Sign Up"/>
</form>
);
}

}

export default withRouter(LoginForm);

Suggestion

As written, the LoginForm component will always be rendered, this is probably not what you intended. I suggest just moving it into a route so the route props will be passed.

<Route path="/login" component={LoginForm} />

React Router history.push() changed url but did not rendered components content

You need hook useHistory from 'react-router-dom' but not from package 'history'

import { useHistory } from 'react-router-dom'
const history = useHistory();
history.push(...);

React router not rendering the component through history.push()

Try this


import { React } from "react";
import { Router as MyRouter, Switch, Route } from "react-router-dom";
import NavBar from "./Components/NavBar";
import Article from "./Components/Article";
import Articles from "./Components/Articles";

const Router = () => {
return (
<div>
<MyRouter >
<NavBar />
<Switch>
<Route exact path="/" component={Articles} />
<Route exact path="/home/:title/:id" component={Article} />
</Switch>
</MyRouter>
</div>
);
};

Since you are using Router as a wrapper of your application, what if you don't pass the history object on the Router since you will be using the react hook useHistory().



Related Topics



Leave a reply



Submit