Apologies if it sounds stupid question. I'm trying to redirect user to the content pages of the app when they are signed in. My current implementation correctly changes the URL in the address bar but doesn't render a component and shows a blank page. Is there something i'm missing in here. Could you please suggest what am i'm missing ?
I tried googling it but couldn't make it work. Here are some of the links i tried.
1- https://tylermcginnis.com/react-router-programmatically-navigate/
2- https://learnwithparam.com/blog/redirect-route-in-react-router/
3- Programmatically navigate using react router
Login Component
class Login extends React.Component {
handleSubmit(e) {
e.preventDefault();
var value = true;
localStorage.setItem('userLoggedIn', JSON.stringify(value));
this.setState({
toUsers: true
})
//this.props.history.push('/users')
}
render() {
if (this.state.toUsers === true) {
return <Redirect to="/users" />
}
return (
<div className="Login">
<form onSubmit={this.handleSubmit}>
Email
<input
type="text"
value={this.state.email}
onChange={this.handleEmailChange}
/>
Password
<input
type="text"
value={this.state.password}
onChange={this.handlePasswordChange}
/>
<button>
Login
</button>
</form>
</div>
);
}
}
export default Login;
App component:
import React, { Component } from 'react';
import { BrowserRouter, HashRouter, Switch, Route } from 'react-router-dom';
import Navigation from './common/Navigation';
import Users from './users/Users.jsx';
import Roles from './roles/Roles.jsx';
import ProtectedRoute from './authentication/ProtectedRoute.jsx';
export default class App extends Component {
constructor(props) {
super(props);
}
render() {
return <Navigation>
<Switch>
<Route exact path="/users" component={Users} />
<Route exact path="/roles" component={Roles} />
<Route render={() => <div>Not Found</div>} />
</Switch>
</Navigation>
}
}
Component which decides if login page should be displayed or the content pages of the application
export default class AppAuthenticated extends React.Component {
constructor(props) {
super(props);
this.state = {
email: "",
password: "",
toUsers: false,
isUserLoggedIn: false
};
}
componentDidMount() {
const isUserLoggedIn = JSON.parse(localStorage.getItem('userLoggedIn'));
this.setState({
isUserLoggedIn: isUserLoggedIn
});
}
render() {
return (
<>
{this.state.isUserLoggedIn ? (
<App />
) : (
<Login />
)}
</>
);
}
}
Main App Component which renders the app
import React from 'react'
import ReactDOM from "react-dom";
import App from './pages/App'
import { BrowserRouter, HashRouter, Switch, Route } from 'react-router-dom';
import PropTypes from "prop-types";
import { withRouter } from "react-router";
import Login from './pages/authentication/Login.jsx';
import AppAuthenticated from './pages/authentication/AppAuthenticated.jsx';
ReactDOM.render(
<HashRouter>
<AppAuthenticated />
</HashRouter>,
document.getElementById("root")
);
Expected Behavior: When user is signed in successfully, they should be redirected to the "App" component where I've defined all the routes that logged in user can access
In your app.js, pass a prop to your login component that can change the container state to render your content pages.
setLoggedIn = ()=>{
this.setState({isUserLoggedIn: true});
}
render() {
return (
<>
{this.state.isUserLoggedIn ? (
<App />
) : (
<Login loginSuccess={this.setLoggedIn} />
)}
</>
);
}
Then in your Login Component...
handleSubmit(e) {
e.preventDefault();
var value = true;
localStorage.setItem('userLoggedIn', JSON.stringify(value));
this.setState({
toUsers: true
})
this.props.loginSuccess();
//this.props.history.push('/users')
}
setLoggedIn = ()=>{
this.setState({isUserLoggedIn: true});
}
render() {
return (
<>
{this.state.isUserLoggedIn ? (
<App />
) : (
<Login loginSuccess={this.setLoggedIn} />
)}
</>
);
}
this is missing in your passing props #maddy
Related
Can anyone please tell me how redirect works in react?
I'm from Angular background and trying to learn React.
I have a small application, where I want to redirect the user to profile component on click of a button.
This is a very basic requirement but there's no proper documentation available (or I'm not able to find one).
Any help will be appreciated.
Here's my code:
import { BrowserRouter, Route, Switch, Redirect } from "react-router-dom";
<BrowserRouter>
<Switch>
<Route path="/" exact render={props => <Index {...props} />} />
<Route path="/login-page" exact render={props => <Login {...props} />} />
<Route path="/profile-page" exact render={props => <Profile {...props} />} />
<Route path="/dashboard" exact render={props => <Dashboard {...props} />} />
<Redirect to="/" />
</Switch>
</BrowserRouter>,
<Button color='primary' onClick={e => this.viewProfile()}>View Profile</Button>
viewProfile = function () {
console.log("clicked");
}
Use the Link component from react-router-dom:
<Link to="/profile">View profile</Link>
Check out the docs as well, they provide a lot of examples
You can call up the useHistory() hook to navigate.
import { useHistory } from "react-router-dom";
const yourComponent = () => {
const history = useHistory();
viewProfile = function() {
history.push("/new-url");
};
return (
<Button color="primary" onClick={viewProfile}>
View Profile
</Button>
);
};
If you are using a class component you can wrap it withRouter() to gain access to the same history object.
import React from "react";
import { withRouter } from "react-router";
class YourComponent extends React.Component {
viewProfile = function() {
const { history } = this.props;
history.push("/new-url");
};
render() {
return (
<Button color="primary" onClick={this.viewProfile}>
View Profile
</Button>
);
}
}
export default withRouter(YourComponent);
React Router comes with a Link component that can be used for this. Just import it, set the destination in the to prop, and wrap your Button with it:
import { Link } from "react-router-dom";
<Link to='/profile-page'>
<Button color='primary' onClick={e => this.viewProfile()}>View Profile</Button>
</Link>
viewProfile = function () {
console.log("clicked");
}
Have a look at the React Router docs, they've got a lot of useful info!
Adding this solved my problem.
import { Redirect } from 'react-router'
state = {
redirect: false
}
const { redirect } = this.state;
if (redirect) {
return <Redirect to='/profile-page'/>;
}
viewProfile = function () {
console.log("clicked");
this.setState({ redirect: true })
}
thanks #tarzen chugh, #Good Samaritan for you response
Nomenclature is important when you are first learning a new framework :)
Strictly speaking a redirect will
Rendering a <Redirect> will navigate to a new location. The new location will override the current location in the history stack
What you want is navigating to the profile page. There are two ways you can achieve this
Through JSX Link.
Programmatically with the history object.
import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';
class Test extends Component{
constructor(props){
super(props);
this.state ={}
}
viewProfile = () => {
const {history} = this.props;
history.push('profile-page');
}
}
import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';
class Test extends Component{
constructor(props){
super(props);
this.state ={}
}
viewProfile = () => {
const {history} = this.props;
history.push('profile-page');
}
render(){
return(
<Button
color='primary'
onClick={() => this.viewProfile()}>
View Profile
/>
);
}
}
export default withRouter(Test);
Here you can use withRouter HOC to do redirects.
import { withRouter } from "react-router";
class Component extends React.Component {
viewProfile = function () {
console.log("clicked");
this.props.history.push('/main');
}
render(){
return (
<Button color='primary' onClick={e => this.viewProfile()}>View Profile</Button>
)
}
}
export default withRouter(Component);
Ref: https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/withRouter.md
Do like this :-
<Button color='primary' onClick={() => this.viewProfile()}>View Profile</Button>
viewProfile= () => {
return (
<Redirect
to={{
pathname: "/profile-page",
}}
/>
);
};
I am new to React and I am trying to display a react route using react router dom but the matching component is not coming up. I have looked at answers from other OS questions related but no help. Here is my app.js file
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import AdminLogin from './AdminLogin';
export default class AdminApp
extends Component {
render() {
return (
<Router>
<Switch>
<div className="App">
<Route path="/" exact={true} component = {AdminLogin} />
</div>
</Switch>
</Router>
);
}
}
if (document.getElementById('app')) {
ReactDOM.render(<AdminApp />, document.getElementById('app'));
}
When i inspect my html file i get this
<div id="app">
<div class="App" location="[object Object]" computedmatch="[object Object]"></div>
</div>
this is my adminLogin component
import React, {Component} from 'react';
import {adminLogin} from './UserFunctions';
export default class AdminLogin extends Component {
constructor(){
super()
this.state = {
email: '',
password: '',
errors: {}
}
this.onChange = this.onChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
}
onChange(e){
this.setState({ [e.target.name]: e.target.value })
}
onSubmit(e){
e.preventDefault;
const user = {
email : this.state.email,
password : this.state.password
}
adminLogin(user).then(res => {
if(res) {
this.props.history.push('/dashboard');
}
})
}
render(){
return (
<div className="kt-grid kt-grid--ver kt-grid--root">
<div className="kt-grid kt-grid--hor kt-grid--root kt-login>
<h1>Login Now</h1>
</div>
</div>
)
}
}
This only works if you are trying the root path (/). If you need to render this component at a subpath (e.g. /foo/bar/login), that should be defined as the route path.
<Route path="/foo/bar/login" exact={true} component = {AdminLogin} />
I have a NavBar that's on multiple pages that allows me to search for an user. After the searching, clicking on it should re-direct me to the user page via Link from react-router-dom. The problem is this only works on the main page, and doesn't work when clicking from an user's page.
I've tried adding WithRouter to all my related components (containers, parents, and the children). Also tried fiddling with the Route pathing / exact pathing syntax to no avail.
*Removed components imports and non-relavant code for clarity.
/app.jsx
import React from 'react';
import { Provider } from 'react-redux';
import {
Route,
Redirect,
Switch,
Link,
HashRouter,
withRouter,
} from 'react-router-dom';
import { AuthRoute, ProtectedRoute } from '../utils/route_utils';
const App = () => (
<div className="main">
<HashRouter>
<Switch>
<AuthRoute exact path="/" component={LoginContainer} />
<AuthRoute exact path="/signup" component={SignupContainer} />
<ProtectedRoute exact path="/feed" component={FeedContainer} />
<ProtectedRoute exact path="/users/:userId" component={GreetingContainer} />
<ProtectedRoute exact path="/posts/:postId" component={PostShowContainer} />
<ProtectedRoute exact path="/upload" component={Upload} />
<ProtectedRoute exact path="/accounts/edit" component={AccountsEditContainer} />
<Route component={Errors} />
</Switch>
</HashRouter>
</div>
);
export default withRouter(App);
/greeting_container.jsx
import React from 'react';
import { withRouter } from 'react-router-dom';
const mapStateToProps = (state, ownProps) => {
...
};
const mapDispatchtoProps = (dispatch) => ({
....
});
export default withRouter(connect(mapStateToProps, mapDispatchtoProps)(Greeting));
/greeting.jsx
import React from 'react';
import { Link } from 'react-router-dom';
import NavBarContainer from '../nav_bar/nav_bar_container';
class Greeting extends React.Component {
constructor(props) {
super(props);
this.renderPosts = this.renderPosts.bind(this);
}
render() {
if (!this.props.user) return null;
const profileUser = parseInt(this.props.match.params.userId)
const user = this.props.user
return (
<div className="user-page-container">
<Modal currentUser={user} />
<NavBarContainer />
</div>
}
export default Greeting;
/nav_bar_container.jsx
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import NavBar from './nav_bar';
const mapStateToProps = (state) => {
....
};
const mapDispatchToProps = (dispatch) => ({
...
});
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(NavBar));
/nav_bar.jsx
import React from 'react';
import { Link, withRouter } from 'react-router-dom';
import NavBarSearch from './nav_bar_search';
const NavBar = (props) => {
return (
<div>
< NavBarSearch
fetchSearchedUsers={props.fetchSearchedUsers}
searchResults={props.searchResults}
/>
</div>
);
};
export default withRouter(NavBar);
/nav_bar_search.jsx
import React from 'react';
import NavBarSearchResults from './nav_bar_search_results';
import { withRouter } from 'react-router-dom';
class NavBarSearch extends React.Component {
constructor(props) {
super(props);
this.state = {
searchInput: ''
}
this.generateSearch = this.generateSearch.bind(this)
}
render() {
return (
<div >
<input className="search-bar-test" onChange={this.generateSearch} type="text" placeholder='Search' />
{this.state.searchInput ? <NavBarSearchResults
searchResults={this.props.searchResults}
handleClick={this.handleClick}
// key={this.props.searchResults.id}
/> : null
}
</div>
)
}
}
export default withRouter(NavBarSearch);
/nav_bar_search_results.jsx
import React from 'react';
import { withRouter, Link } from 'react-router-dom';
const NavBarSearchResults = function({ searchResults, handleClick }) {
let results = searchResults ? Object.entries(searchResults).map((result) =>
<Link className="search-links" to={`/users/${result[0]}`} onClick={e => handleClick} key={result[0]}>
<div className="search-results-item">
<img className="search-results-image" src={result[1].photoUrl} />
{result[1].username}
</div>
</Link>
) : null
return (
<div className="search-results-container">
{results}
</div>
);
}
export default withRouter(NavBarSearchResults);
Once again, it is the Link in the Nav_Bar_Search_Results.jsx file that is not working. This file is nested several levels deep starting from the Greeting.jsx file. The string interpolation I'm doing is the user_id number. Please recall that the url parameters updates and the page works if I CLICK and REFRESH page, but not on first click alone.
The correct user page should render as specified by the url parameters
I tried to summarize my problem in the code below. My real code it is too big, so I developed another similar code in way to solve this problem.
I have a loggin page where the user has to type "True" or "False" to update the state fom this component.
I need to pass this state to the "Route" component, that will verify what page is going to be rendered. If the parameter received from the Login component is "True", then Home page will be rendered. If it is "False" Login page will be rendered.
From my understanding, I have to pass props from childreen to parent component, but I am not being able to solve this problem.
App.jsx (Component)
import React from 'react'
import { BrowserRouter } from 'react-router-dom'
import Routes from './Routes'
export default props =>
<BrowserRouter>
<div>
<Routes />
</div>
</BrowserRouter>
Routes.jsx (Component)
import React from 'react'
import { Switch, Route, Redirect} from 'react-router'
import Login from './Login';
import Home from './Home';
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route
{...rest}
render={props =>
isAuthenticated() ? (
<Component {...props} />
): (
<Redirect to={{pathname: "/login", state: {from: props.location }}} />
)}
/>
)
const Routes = () =>
<Switch>
<PrivateRoute path='/home' component={Home} />
<Route exact path='/login' component={Login} />
<Redirect from='*' to='/home' />
</Switch>
export default Routes;
Home.jsx (Component)
import React, { Component } from 'react'
export default class Home extends Component {
render (){
return (
<h1>This is the HOME page</h1>
);
}
}
Login.jsx (Component)
import React, { Component } from 'react'
import { Link } from 'react-router-dom'
import Routes from './Routes'
export default class Login extends Component{
constructor(props){
super(props);
this.state = {
userLoggedIn: false
}
this.handleChange = this.handleChange.bind(this)
}
handleChange(e) {
e.preventDefault()
let stateName = e.target.name
let stateValue = e.target.value
console.log(e.target, e.target.value)
this.setState({ [stateName]: stateValue})
}
render(){
console.log('esse estado é',this.state.userLoggedIn)
return(
<div>
<div>This is the Login Page</div>
<label>State Test</label>
<input placeholder="true or false" onChange={e => this.handleChange(e)}></input>
<Link to={{pathname: "/home"}}>
<button >Go To Home Page</button>
</Link>
</div>
)
}
}
What do I expect?
When the user type "True" in the Login page, Home page must be rendered.
When the user type "False" in the Login page, Login page must be rendered.
Here is the working code and codesandbox link
If user enters false, then he will not be redirected as he is already on Login page.
import React, { Component } from "react";
import {
BrowserRouter as Router,
Route,
Link,
Redirect,
withRouter
} from "react-router-dom";
function AuthExample() {
return (
<Router>
<div>
<AuthButton />
<ul>
<li>
<Link to="/home">Home Page</Link>
</li>
</ul>
<Route path="/login" component={Login} />
<PrivateRoute path="/home" component={Home} />
</div>
</Router>
);
}
const fakeAuth = {
isAuthenticated: false,
authenticate(cb) {
this.isAuthenticated = true;
setTimeout(cb, 100); // fake async
},
signout(cb) {
this.isAuthenticated = false;
setTimeout(cb, 100);
}
};
const AuthButton = withRouter(({ history }) =>
fakeAuth.isAuthenticated ? (
<p>
Welcome!{" "}
<button
onClick={() => {
fakeAuth.signout(() => history.push("/"));
}}
>
Sign out
</button>
</p>
) : (
<p>You are not logged in.</p>
)
);
function Home() {
return <h3>This is the HOME page</h3>;
}
function PrivateRoute({ component: Component, ...rest }) {
return (
<Route
{...rest}
render={props =>
fakeAuth.isAuthenticated ? (
<Component {...props} />
) : (
<Redirect
to={{
pathname: "/login",
state: { from: props.location }
}}
/>
)
}
/>
);
}
class Login extends Component {
state = {
redirectToReferrer: false,
input: ""
};
login = e => {
e.preventDefault();
if (this.state.input === "true") {
fakeAuth.authenticate(() => {
this.setState({ redirectToReferrer: true });
});
} else {
this.setState({ redirectToReferrer: true });
}
};
handleChange = ({ target }) => {
this.setState({ [target.name]: target.value });
};
render() {
let { from } = this.props.location.state || { from: { pathname: "/" } };
let { redirectToReferrer } = this.state;
if (redirectToReferrer) return <Redirect to={from} />;
return (
<div>
<div>This is the Login Page</div>
<label>State Test</label>
<br />
<input
placeholder="true or false"
name={"input"}
value={this.state.input}
onChange={this.handleChange}
/>
<br />
<button onClick={this.login}>Go To Home Page</button>
</div>
);
}
}
export default AuthExample;
Hope that helps!!!
I'm trying to trigger a redirect if a user is logged in. A successful login triggers an update of this.state.user so I'd like to handle the redirect in componentDidUpdate() or another lifecycle method.
The if statement is getting called when I intend for it to, but the redirect does nothing. Any idea as to how I can fix this? I just want this to update the url so it doesn't necessarily need to use Redirect.
I'm not using user authentication currently and don't intend to add it yet.
import React, { Component } from "react";
import "./App.css";
import { BrowserRouter as Router, Route, Redirect } from "react-router-dom";
import AuthContainer from "./components/AuthContainer";
import ChatSelector from "./components/ChatSelector";
import { debug } from "util";
// import ChatRoomContainer from './components/ChatRoomContainer';
class App extends Component {
constructor(props) {
super(props);
this.state = {
user: {}
};
}
setUser = user => {
console.log("setting user");
this.setState({ user });
};
componentDidUpdate() {
// if a user is logged in, redirect them to chat-selector
if (Object.keys(this.state.user).length > 0) {
console.log(this.state.user);
<Router>
<Redirect to="/chat-selector" />;
</Router>;
}
}
render() {
return (
<Router>
<div>
<Route
exact
path="/"
render={props => (
<AuthContainer {...props} setUser={this.setUser} />
)}
/>
<Route
exact
path="/chat-selector"
render={props => <ChatSelector {...props} user={this.state.user} />}
/>
{/* <Route exact path='/chatroom' component={ChatRoomContainer}/> */}
</div>
</Router>
);
}
}
export default App;
I solved this by placing the if statement within render, and adding a redirect boolean to state.
import React, { Component } from "react";
import "./App.css";
import {
BrowserRouter as Router,
Route,
Redirect,
withRouter
} from "react-router-dom";
import AuthContainer from "./components/AuthContainer";
import ChatSelector from "./components/ChatSelector";
import { debug } from "util";
// import ChatRoomContainer from './components/ChatRoomContainer';
class App extends Component {
constructor(props) {
super(props);
this.state = {
user: {},
redirect: false
};
}
setUser = user => {
console.log("setting user");
this.setState({ user });
};
redirect = () => {
this.setState({ redirect: true });
};
render() {
if (
Object.keys(this.state.user).length > 0 &&
this.state.redirect === true
) {
this.setState({ redirect: false });
console.log("logged in");
return (
<Router>
<Redirect to="/chat-selector" />
</Router>
);
} else {
console.log("not logged in");
}
return (
<Router>
<div>
<Route
exact
path="/"
render={props => (
<AuthContainer
{...props}
setUser={this.setUser}
redirect={this.redirect}
/>
)}
/>
<Route
exact
path="/chat-selector"
render={props => <ChatSelector {...props} user={this.state.user} />}
/>
{/* <Route exact path='/chatroom' component={ChatRoomContainer}/> */}
</div>
</Router>
);
}
}
export default App;
There is actually a better way of doing this, and I have recently stumbled across a similar situation.
Since the <Redirect /> technique does not work well with helper functions or lifecycle methods, I suggest to instead use this.props.history.push() inside the ComponentDidUpdate() to perform a redirect. Just remember to wrap your component with the withRouter() HOC.
Example code here: http://blog.jamesattard.com/2018/03/fire-action-creator-after-react-state.html