I want to redirect to the home page when some condition returns null or false but the action of Redirect is not triggered.
import { Link, Redirect } from "react-router-dom";
if(localStorage.getItem("example") === null || localStorage.getItem("example") === false){
return <Redirect to="/" />
}
I put this code inside in a simple function triggered in one OnClick and componentDidMount(), but it's not working.
You could use Redirect to home page, based on redirect flag that could be changed by using setState in onClickHandler or handleSubmit.
import { Redirect } from "react-router-dom";
class MyComponent extends React.Component {
state = {
redirect: false
}
handleSubmit () {
if(localStorage.getItem("example") === null || localStorage.getItem("example") === false){
return this.setState({ redirect: true });
}
}
render () {
const { redirect } = this.state;
if (redirect) {
return <Redirect to='/'/>;
}
return <YourForm/>;
}
You need to use the Redirect inside render. It is a React Component which renders and then sends the user to the desired path:
import React, { Component } from "react";
import { Route, Switch, Redirect } from "react-router-dom";
class RootPage extends React.Component {
state = {
isLoggedOut: false
};
onClick = () => {
this.setState({
isLoggedOut: true
});
};
render() {
return (
<div>
{this.state.isLoggedOut && <Redirect to="/logout" />}
<button onClick={this.onClick}>Logout</button>
</div>
);
}
}
const Log = () => <h1>Logout</h1>;
class App extends Component {
render() {
return (
<div>
<nav className="navbar navbar" />
<Switch>
<Route exact path="/" component={RootPage} />
<Route exact path="/logout" component={Log} />
</Switch>
</div>
);
}
}
export default App;
When you click on the logout button it will redirect you to the rootPath.
Here is the Demo: https://codesandbox.io/s/q9v2nrjnx4
Have a look at this example in the official docs.
<Redirect /> should be inside your render method if you use a class component. or if you use a function component it should be in what's returned by it.
Example bellow:
import { Component } from 'react';
const PrivateComponent = (props) => {
return(
localStorage.getItem("example")
? <RandomComponent />
: <Redirect to="/signin" />
)
}
Related
Hi I have been developing this application using react and react-router-dom The Page component is wrapped by a HOC that imports a contentservice to access a rest api.
My navigation is in the App component. The relevant part is the
<Link to="/page/123">About Page</Link>
and
<Link to="/page/456">Contact Page</Link>
When these links are clicked the page doesn't redraw as i expected. First time i go to 'About Page' it's all good. Then when i click to go to 'Contact Page' nothing changes. Then i click on the 'About Page' again and the 'Contact Page' shows.
In all the cases above the browser address bar shows the right path and if i refresh the page i go to the right page.
Here is my navigation page:
import React, { Component } from "react";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
import { connect } from "react-redux";
import Home from "./Home";
import Admin from "./Admin";
import Members from "./Members";
import Login from "./Login";
import Page from "./Page";
import PrivateRoute from "./PrivateRoute";
import "./App.css";
class App extends Component {
render() {
return (
<Router>
<div>
<ul>
<li>
<Link to="/">Home Page</Link>
</li>
<li>
<Link to="/page/123">About Page</Link>
</li>
<li>
<Link to="/page/456">Contact Page</Link>
</li>
<li>
<Link to="/members">Members</Link>
</li>
<li>
<Link to="/admin">Admin</Link>
</li>
</ul>
</div>
<Switch>
<Route path="/login" component={Login} />
<Route path="/page/:id" component={Page} />
<Route exact path="/" component={Home} />
<PrivateRoute path="/members">
<Members />
</PrivateRoute>
<PrivateRoute path="/admin">
<Admin />
</PrivateRoute>
</Switch>
</Router>
);
}
}
const mapStateToProps = (state) => {
return {
isLoggedIn: state.isLoggedIn,
};
};
export default connect(mapStateToProps, null)(App);
This is my page component:
import React, { Component } from "react";
import WithBackend from "./WithBackend";
class Page extends Component {
constructor(props) {
super(props);
this.resource = "/pages/";
this.state = { model: null };
}
render() {
if (this.state.model != null) {
return (
<div className="container">
<div className="row">
<div className="col-md">
<h1>{this.state.model.title}</h1>
<h2 dangerouslySetInnerHTML={{ __html: this.state.model.body }} />
</div>
</div>
</div>
);
} else {
return (
<div>
<h2>Page id: {this.props.match.params.id}</h2>
</div>
);
}
}
componentDidMount() {
this.props
.getEntity(this.resource, this.props.match.params.id)
.then((model) => this.setState({ model }));
}
componentDidUpdate(nextProps) {
if (nextProps.match.params.id !== this.props.match.params.id) {
this.props
.getEntity(this.resource, nextProps.match.params.id)
.then((data) => {
this.setState({ model: data });
});
}
}
}
export default WithBackend(Page);
This is the Withbackend HOC:
import React from "react";
import ContentService from "./ContentService";
const WithBackend = (WrappedComponent) => {
class HOC extends React.Component {
constructor() {
super();
this.contentService = new ContentService();
this.getEntity = this.getEntity.bind(this);
this.getEntities = this.getEntities.bind(this);
}
getEntity(resource, id) {
return this.contentService
.getEntity(resource, id)
.then((response) => response.json())
.catch((e) => {
console.log(e);
});
}
getEntities(resource) {
return this.contentService
.getEntities(resource)
.then((response) => response.json())
.catch((e) => {
console.log(e);
});
}
render() {
return (
<WrappedComponent
getEntity={this.getEntity}
getEntities={this.getEntities}
{...this.props}
/>
);
}
}
return HOC;
};
export default WithBackend;
And the content service:
class ContentService {
baseUrl = "http://localhost:8080";
getEntity(resource, id) {
const path = this.baseUrl + resource + id;
const fetchPromise = fetch(path, {
method: "GET",
});
return Promise.resolve(fetchPromise);
}
getEntities(resource) {
const fetchPromise = fetch(this.baseUrl + resource, {
method: "GET",
});
return Promise.resolve(fetchPromise);
}
}
export default ContentService;
Has anyone got any ideas why this is happening? I am not sure if it has anything to do with the Page component being wrapped by HOC but just thought it is worth mentioning.
Thank you.
Issue
The componentDidUpdate lifecycle method receives the previous props, state, and snapshot values, not the next ones.
componentDidUpdate
componentDidUpdate(prevProps, prevState, snapshot)
By sending the "previous" props' match param id you were a "cycle" behind.
Solution
Use the current id value from props.
componentDidUpdate(prevProps) {
if (prevProps.match.params.id !== this.props.match.params.id) {
this.props
.getEntity(this.resource, this.props.match.params.id)
.then((data) => {
this.setState({ model: data });
});
}
}
I am facing issue in passing state from App Component through React Router. In the App component's ComponentwillMount function, the state is loaded through an API, which is passed to Login Component by specifying it in the render function of the Route Component.
But, the Login Component is loaded prior to App setState. I need to pass this state to all other Components. Please help !
import React, { Component } from 'react';
class App extends Component {
constructor(props) {
super(props);
this.state = {
language: 'en',
labels: null,
};
}
componentDidMount() {
let language = getLanguage(); //from url
this.setState({ language }, async () => {
await this.getLabels();
});
}
getLabels = () => {
//Hit Api to fetch labels on basis of language set
this.setState({ labels: data });
};
render() {
return (
<div className='App'>
<Router>
<Switch>
<Route
exact
path='/'
render={(props) => (
<Login labels={this.state.labels} {...props} />
)}
/>
</Switch>
</Router>
</div>
);
}
}
export default App;
import React, { Component } from 'react';
export default class Login extends Component {
render() {
console.log(this.props.labels);
}
}
this.props.labels is undefined in Login Component.
Can you try showing a loder untill your api call was successfull.
import React, { Component } from 'react';
class App extends Component {
constructor(props) {
super(props);
this.state = {
language: 'en',
labels: null,
fetchingLabels:true
};
}
componentDidMount() {
let language = getLanguage(); //from url
this.setState({ language }, async () => {
await this.getLabels();
});
}
getLabels = () => {
//Hit Api to fetch labels on basis of language set
this.setState({ labels: data, fetchingLabels:false });
};
render() {
if(this.state.fetchingLabels){
return 'I am loading' // you can add your loader here
}
return (
<div className='App'>
<Router>
<Switch>
<Route
exact
path='/'
render={(props) => (
<Login labels={this.state.labels} {...props} />
)}
/>
</Switch>
</Router>
</div>
);
}
}
export default App;
import React, { Component } from 'react';
export default class Login extends Component {
render() {
console.log(this.props.labels);
}
}
I'm coding login function that would return to home component after finish save token data to client.
However, when it go back to homepage, i can not use setState because do not have any componentWillMount or componentDidMount function was called.
Login.js
axios.post(`${Config.API_URL}/users/login`, param)
.then(response => {
if (response) {
this.setState({
errorCode : response.status
});
}
if(response.status===ErrorCode.SUCCESS){
var authorization = {"authorization": response.headers.authorization.toString()}
SessionStorage.saveLocalStorage(authorization);
this.props.history.push("/");
}
})
App.js
componentWillMount() {
if(SessionStorage.isAuthorization()){
this.setState({
isAuthorization : true
});
}
console.log('Component Will MOUNT!')
}
ComponentWillMount() never been called so can not set value for isAuthorization = true anyway.
A couple of notes:
1) I'm assuming App.js is actually the holder of all your routes. In this case, App.js is never unmounted from ReactDOM. What you want to do is define a separate component for your "/" route, keeping your App.js clean, focusing only on rendering routes. We will call this component Home.
2) I'm not familiar with any library that uses SessionStorage(). You might be looking for the sessionStorage native JS library. This will have methods called setItem and getItem for storing and retrieving your token.
3) With the separate Home component, we can call componentDidMount() to retrieve the token.
With that in mind we can do the following as a template on how you can achieve the functionality you're looking for: https://codesandbox.io/s/suspicious-leftpad-moqs2
App.js
class App extends React.Component {
render() {
return (
<Provider store={store}>
<BrowserRouter>
<div>
<Switch>
<Route path="/" component={Home} exact />
<Route path="/login" component={Login} />
</Switch>
</div>
</BrowserRouter>
</Provider>
);
}
}
Home.js
import React from "react";
import { Link } from "react-router-dom";
class Home extends React.Component {
state = {
token: ""
};
componentDidMount() {
const token = sessionStorage.getItem("authorization");
if (token) {
this.setState({
token: token
});
}
}
render() {
return (
<div>
<Link to="/login">Login</Link>
<p>{this.state.token}</p>
</div>
);
}
}
export default Home;
Login.js
import React from "react";
class Login extends React.Component {
handleClick = () => {
var token = 210;
sessionStorage.setItem("authorization", token);
this.props.history.push("/");
};
render() {
return (
<div>
<button onClick={this.handleClick}>Login</button>
</div>
);
}
}
export default Login;
please, what is the best way in React how to achieve:
submit form (and..)
redirect to another page (and..)
have some props from the origin form here?
I have discovered two possibilities how to redirect:
Source article: https://tylermcginnis.com/react-router-programmatically-navigate/
1) with React Router: history.push()
2) with React Router: <Redirect />
1) With history.push(): Redirecting works but i have no idea how to add custom props to redirected page.
2) With <Redirect />: adding custom props works (in this way):
<Redirect to={{ pathname: '/products', state: { id: '123' } }} />
But redirecting does not work to me, I keep receiving errors after submission.
Source code:
import React from 'react';
import './App.css';
import { withRouter, Redirect } from 'react-router-dom'
class App extends React.Component {
state = {
toDashboard: false,
}
handleSubmit = () => {
this.setState(() => ({
toDashboard: true
}));
}
render() {
if (this.state.toDashboard === true) {
return <Redirect to={{
pathname: '/products', state: { id: '123' }
}} />
}
return (
<div>
<h1>Register</h1>
<form onSubmit={this.handleSubmit}>
<button type="submit">Submit</button>
</form>
</div>
);
}
}
export default withRouter(App);
Errors:
Warning: You tried to redirect to the same route you're currently on: /products"
Form submission canceled because the form is not connected
What is the best way how to achieve my target, please?
You need to cancel the default submit action.
so change you handleSubmit method to
handleSubmit = (e) => {
e.preventDefault();
this.setState({
toDashboard: true
});
}
What is finally working fine to me is code below here.
From App.js it is routed to Products.js, then i click on the button and it is redirected to NotFound.js and i can reach props "state: { id: 123 }" and i display it here.
Hope it will help to someone who is looking for some working submission patern.
App.js
import React from 'react';
import './App.css';
import { Route, Switch } from 'react-router-dom';
import Products from './Products';
import NotFound from './NotFound';
import Home from "./Home";
class App extends React.Component {
render() {
return (
<div>
<Switch>
<Route path="/products" component={Products} />
<Route path="/notfound" component={NotFound} />
<Route path="/" exact component={Home} />
</Switch>
</div>
);
}
}
export default App;
Products.js
import React, { Component } from "react";
class Products extends Component {
handleSubmit = (e) => {
e.preventDefault();
this.props.history.push({ pathname: '/notfound', state: { id: 123 } });
}
render() {
console.log(this.props);
return (
<div>
<h1>Products</h1>
<form onSubmit={this.handleSubmit}>
<button type="submit">Submit</button>
</form>
</div>
);
}
}
export default Products;
NotFound.js
import React from "react";
const NotFound = (props) => {
console.log(props);
return (
<div>
<h1>Not Found</h1>
<h2>{props.location.state.id}</h2>
</div>
);
};
export default NotFound;
For some reason, my routes only render half the time - seems like a race condition of some sort. It'll print out the "OK" but nothing from the routes, not even the 404. Pretty clear cut.
If I remove the loading bit it'll always render the switch block as intended.
Is there a better / different way to do this?
v4.2.0
render() {
const { hasInitialized } = this.props;
if (!hasInitialized) {
return (
<div>Loading...</div>
);
}
return (
<div style={{ height: '100%', width: '100%' }}>
<Helmet titleTemplate="%s - Resilient" defaultTitle="Resilient" />
<div>OK</div>
<Switch>
<Redirect from="/" to="/auth/check" exact={true} />
<Route path="/auth" component={AuthLayout} />
<AuthenticatedRoute path="/x" component={AdminLayout} />
<Route component={Miss404} />
</Switch>
</div>
);
}
https://github.com/ReactTraining/react-router/issues/5621
I read the react-router docs many times, and the part about Blocked Updates seems relevant. But, when I put a debugger line in <Layout />, location and history always have the right info, and still, none of the routes would render.
I still don't understand what the issue was, but here's the workaround I came up with. The code below wraps my <Layout /> component, which contains all the routes.
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import LocalStorageManager from 'utils/LocalStorageManager';
import { selectCurrentUser, selectHasInitialized } from 'client/selectors/authSelectors';
import { setAccessToken, getProfile } from 'shared/api';
import { setHasInitialized, signIn } from 'modules/auth/actions.js';
import SinglePageCard from 'components/layout/SinglePageCard';
const mapStateToProps = (state) => {
return {
currentUser: selectCurrentUser(state),
hasInitialized: selectHasInitialized(state),
};
};
export default (WrappedComponent) => {
class Layout extends Component {
componentWillMount() {
const accessToken = LocalStorageManager.getAccessToken();
if (!accessToken) {
this.props.setHasInitialized();
return;
}
setAccessToken(accessToken);
getProfile().then((response) => {
console.log(response);
const { user } = response.data.data;
this.props.signIn(user);
}).catch((error) => {
console.log(error);
this.props.setHasInitialized();
});
}
render() {
const { currentUser, hasInitialized, ...rest } = this.props;
if (!hasInitialized) {
return (
<SinglePageCard>
<div>Initializing...</div>
</SinglePageCard>
);
}
return (
<WrappedComponent {...rest} />
);
}
}
return withRouter(connect(mapStateToProps, { setHasInitialized, signIn })(Layout));
};