Pass state from one component to another stateless component - reactjs

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!!!

Related

Link & useHistory.push from 'react-router-dom' changes URL but Not reloading page

I would like to have a button to redirect to homepage on 404 and errorBoundary pages.
I am getting history object and url changes on button click.
However, it does not reload homepage.
Link & useHistory works perfectly on other pages except these two !
tried Router & BrowserRouter both from 'react-router-dom', but none of them worked.
Any advice ? Thank you !!
versions
react 17.0.2
react-dom 17.0.2
react-router-dom 5.2.0
routes.js
import React, { lazy, Suspense } from 'react';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import { createBrowserHistory } from 'history';
const history = createBrowserHistory();
const routes = () => (
<Router history={history}>
<ErrorBoundary>
<Suspense fallback={<Loading />}>
<App />
<Switch>
<Route path="/login" component={Login} />
<Route exact path={['/', '/reserves']} component={ReservesMain} />
<PrivateRoute path="/reserve/:id" component={ReserveMain} />
<PrivateRoute path="/managers" component={ManagersMain} />
<PrivateRoute path="/manager/:id" component={ManagerMain} />
<Route component={NotFound} />
</Switch>
</Suspense>
</ErrorBoundary>
</Router>
);
notFound.js
import { useHistory, Link } from 'react-router-dom';
import Button from '../components/shared/button';
...
export default function NotFound() {
const history = useHistory();
const onRedirect = () => history.push('/');
return (
<div>
<h1>404. Not Found. </h1>
<p>Oops! Something's missing...</p>
<p>The page does not exist or is unavailable.</p>
<Link to={'/'}>redirect button</Link>
<Button name="Go Home" onClick={onRedirect} />
</div>
);
}
errorBoundary.js
import React from 'react';
import { connect } from 'react-redux';
import { useHistory } from 'react-router-dom';
import Button from '../components/shared/button';
const BadRequest = ({ status }) => [
<h1 key="be-1">
{status ? <span>{status}.</span> : null} This page isn't working.
</h1>,
<h2 key="be-2">Your client has issued a malformed or illegal request.</h2>,
<h2 key="be-3">If the problem continues, contact the engineer department.</h2>,
];
const SystemError = ({ status }) => [
<h1 key="se-1">
{status ? <span>{status}.</span> : null} Something went wrong.
</h1>,
<h2 key="se-2">Oops! We're experiencing an internal Server Error.</h2>,
<h2 key="se-3">Please try again later.</h2>,
];
export const ErrorComponents = ({ status }) => {
const history = useHistory();
const onRedirect = () => history.push('/');
return (
<>
<div style={rootStyle}>
<div style={innerStyles}>
{status === 400 ? <BadRequest status={status} /> : <SystemError status={status} />}
<Button name="Go Home" onClick={onRedirect} />
</div>
</>
);
};
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { error: null };
}
static getDerivedStateFromError(error) {
return { error };
}
componentDidCatch(error, info) {
if (process.env.NODE_ENV === 'development') {
console.log(`error : ${error.name}, message: ${error.message || 'null'}`);
console.log(`info : ${JSON.stringify(info)}`);
}
}
render() {
if (this.state.error) {
return <ErrorComponents status={this.props.asyncApi.status} />;
}
return this.props.children;
}
}
const mapStateToProps = (state) => ({
asyncApi: state.asyncApi,
});
export default connect(mapStateToProps, null)(ErrorBoundary);

React Router TypeError: Cannot read property 'state' of undefined

I am making a basic routing app in react and redux and i am getting an error when i want to redirect to the referrer.
Error happens in the line:
this.referrer = props.location.state.referrer || "/";
This is my Login component.
import React, { Component, createRef } from "react";
import { connect } from "react-redux";
import { Redirect } from "react-router-dom";
import axios from "axios";
import { updateAuthTokens, updateLoggedin } from "./redux/actions/actions";
class Login extends Component {
constructor(props) {
super(props);
this.idRef = createRef();
this.pwdRef = createRef();
this.state = {
isLoggedIn: false,
isError: false,
identifier: "",
password: "",
};
this.postLogin = this.postLogin.bind(this);
this.referrer = props.location.state.referrer || "/";
}
postLogin(e) {
e.preventDefault();
const identifier = this.idRef.current.value;
const password = this.pwdRef.current.value;
axios
.post(process.env.REACT_APP_BASE_URL + "/auth/local", {
identifier,
password,
})
.then((result) => {
if (result.status === 200) {
this.props.ul(true);
this.props.uat(result.jwt);
this.setState({ isLoggedIn: true });
} else {
this.setState({ isError: true });
}
})
.catch((e) => {
this.setState({ isError: true });
});
}
render() {
if (this.state.isLoggedIn) {
return <Redirect to={this.referrer} />;
} else {
return (
<div>
<form>
<input type="username" ref={this.idRef} placeholder="email" />
<input type="password" ref={this.pwdRef} placeholder="password" />
<button onClick={this.postLogin}>Sign In</button>
</form>
{this.state.isError && (
<p>The username or password provided were incorrect!</p>
)}
</div>
);
}
}
}
const mapStateToProps = (state) => {
return {
isLoggedIn: state.isLoggedIn,
};
};
const mapDispatchToProps = {
ul: updateLoggedin,
uat: updateAuthTokens,
};
export default connect(mapStateToProps, mapDispatchToProps)(Login);
Redirect happens from this component:
import React from "react";
import { connect } from "react-redux";
import { Route, Redirect } from "react-router-dom";
class PrivateRoute extends React.Component {
render() {
const { isLoggedIn, children, ...rest } = this.props;
if (isLoggedIn) {
return <Route {...rest}>{children}</Route>;
} else {
return (
<Redirect
to={{
pathname: "/login",
state: { referrer: this.props.location }
}}
/>
);
}
}
}
const mapStateToProps = (state) => {
return {
isLoggedIn: state.isLoggedIn,
};
};
export default connect(mapStateToProps, null)(PrivateRoute);
And my routes are here:
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 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="/members">Members</Link>
</li>
<li>
<Link to="/admin">Admin</Link>
</li>
</ul>
</div>
<Switch>
<Route path="/login">
<Login />
</Route>
<Route exact path="/">
<Home />
</Route>
<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);
I haven't been able to get it to work and have had to comment it out which means i always end up in the home page instead of my intended location.
Any help/insight would be appreciated.
for accessing location as props change:
<Route path="/login">
<Login />
</Route>
<Route exact path="/">
<Home />
</Route>
<PrivateRoute path="/members">
<Members />
</PrivateRoute>
<PrivateRoute path="/admin">
<Admin />
</PrivateRoute>
to
<Route path="/login" component={Login} />
<Route exact path="/" component={Home} />
<PrivateRoute path="/members" component={Members} />
<PrivateRoute path="/admin" component={Admin} />
You are not going to always redirect so change:
this.referrer = props.location.state.referrer || "/";
to
this.referrer = props.location.state ? props.location.state.referrer : "/";

How to hide nav bar component in reactjs using protected route

Hi there i have created a protected route in and passing navbar as parent component. then i passing child components in protected rout i want to hide navbar in some component on some action
Here is my protected.router.js code
import React from "react";
import { Route, Redirect } from "react-router-dom";
import Navbar from './component/Layouts/Navbar';
export const ProtectedRoute = ({
component: Component,
...rest
}) => {
return (
<Route
{...rest}
render={props => {
if (localStorage.getItem("loggedAsli") === "1") {
return <div> <Navbar/>
<Component {...props} /> </div>;
} else {
return (
<Redirect
to={{
pathname: "/login",
state: {
from: props.location
}
}}
/>
);
}
}}
/>
);
};
And this my App.js code
import React from "react";
import { BrowserRouter, Route } from "react-router-dom";
import { ProtectedRoute } from "./protected.route";
import Learn from "./component/Learn/Learn";
class App extends React.Component {
constructor(props) {
super();
this.updateUserLoginSetting = this.updateUserLoginSetting.bind(this);
this.state = {
userId: "",
instituteId: ""
};
}
updateUserLoginSetting = (userId, instituteId) => {
this.setState({ userId: userId, instituteId: instituteId });
};
render() {
return (
<BrowserRouter>
<div className="App">
<Route render={props => <Login />} exact path="/login" />
<ProtectedRoute exact path="/Learn" component={Learn} />
</div>
</BrowserRouter>
);
}
}
export default App;
How i can hide navbar in Learn Component
Please guide me.
is there any global state handling
You could just pass a prop (e.g. showNav) and use it inside ProtectedRoute
export const ProtectedRoute = ({
component: Component,
showNav = true, // added showNav prop
...rest
}) => {
return (
<Route
{...rest}
render={props => { // check if should showNav
if (localStorage.getItem("loggedAsli") === "1" && showNav) {
return <div> <Navbar/>
<Component {...props} /> </div>;
} else {
return (
<Redirect
to={{
pathname: "/login",
state: {
from: props.location
}
}}
/>
);
}
}}
/>
);
};
And pass false when it's Learn
// pass showNav as false to hide nav bar
<ProtectedRoute showNav={false} exact path="/Learn" component={Learn} />

React-HashRouter: Redirect displays blank page

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

Routing to different tabs in ReactJS

There is a HeaderComponent and a BodyComponent in my project.
This is the TabComponent from the HeaderComponent:
TabComponent
import React, {Component} from 'react'
import Context from '../../provider'
import {Nav, NavItem, NavLink} from 'reactstrap'
import {Redirect} from 'react-router-dom'
import classnames from 'classnames'
export default class TabComponent extends Component {
render() {
return (
<Context.Consumer>
{context => (
<Nav tabs color='primary'>
<NavItem>
<NavLink
className={classnames({ active: context.activeTab === '1' })}
onClick={() => { context.toggleTab('1'); }}
>
Home
</NavLink>
</NavItem>
<NavItem>
<NavLink
className={classnames({ active: context.activeTab === '2' })}
onClick={() => { context.toggleTab('2'); }}
>
Popular
</NavLink>
</NavItem>
<NavItem>
<NavLink
className={classnames({ active: context.activeTab === '3' })}
onClick={() => { context.toggleTab('3'); }}
>
All
</NavLink>
</NavItem>
</Nav>
)}
</Context.Consumer>
)
}
}
BodyComponent
import React, {Component} from 'react'
import Context from '../provider'
import SwitchTab from './OtherComponents/SwitchTab'
import { BrowserRouter, Route, Redirect } from "react-router-dom";
export default class BodyComponent extends Component {
render() {
return (
<Context.Consumer>
{context => {
return (
<React.Fragment>
<BrowserRouter>
<React.Fragment>
<Route exact path='/' render={() => <Redirect to='/home/' /> } />
<Route eaxt path='/home/' render={() => context.activetab='1'} />
<Route eaxt path='/popular/' render={() => context.activetab='2'} />
<Route eaxt path='/all/' render={() => context.activetab='3'} />
</React.Fragment>
</BrowserRouter>
<SwitchTab />
</React.Fragment>
)
}}
</Context.Consumer>
)
}
}
This is my provider.js which has the Context
import React, {Component} from 'react';
const Context = React.createContext();
class Provider extends Component {
state = {
loggedIn: false,
username: '',
password: '',
loginModalOpen: false,
signupModalOpen: false,
navbarOpen: false,
activeTab: '3',
toggleLoggedIn: () => {
this.setState({loggedIn: !this.state.loggedIn});
},
toggleLoginModal: () => {
this.setState({loginModalOpen: !this.state.loginModalOpen});
},
toggleSignupModal: () => {
this.setState({signupModalOpen: !this.state.signupModalOpen});
},
toggleNavbar: () => {
this.setState({navbarOpen: !this.state.navbarOpen})
},
toggleTab: (tab) => {
if(this.state.activeTab !== tab) {
this.setState({activeTab: tab});
}
}
}
render() {
return (
<Context.Provider value={this.state}>
{this.props.children}
</Context.Provider>
)
}
}
export {Provider};
export default Context;
And, this is my SwitchTab
import React, {Component} from 'react'
import Context from "../../../provider";
import { Container, TabContent, TabPane } from "reactstrap";
import Home from '../../Content/Home'
import Popular from '../../Content/Popular'
import All from '../../Content/All'
export default class SwitchTab extends Component {
render() {
return (
<Context.Consumer>
{context => {
return (
<Container>
<TabContent activeTab={context.activeTab}>
<TabPane tabId="1">
<Home />
</TabPane>
<TabPane tabId="2">
<Popular />
</TabPane>
<TabPane tabId="3">
<All />
</TabPane>
</TabContent>
</Container>
)
}}
</Context.Consumer>
)
}
}
The functionality that I want to achieve is:
Whenever the URL is '/', redirect to '/home/'.
Whenever the URL is '/home/', change the activeTab from the
Context and make SwitchTab re-render so that home tab gets
opened.
The current working state is whenever I click on a tab, the content changes but the URL remains as /home/. If the Change the URL to /popular/ or /all/, it doesn't change the content.
The error clearly states what is wrong with your problem, you need to wrap your Routes within a div as a Router accepts only one child element.
Also in order to set the state in Provider you need to use setState or provide a handler
class Provider extends Component {
state = {
loggedIn: false,
username: '',
password: '',
loginModalOpen: false,
signupModalOpen: false,
navbarOpen: false,
activeTab: '3',
setActiveTab: (tab) => {
this.setState({activeTab: tab});
}
toggleLoggedIn: () => {
this.setState({loggedIn: !this.state.loggedIn});
},
toggleLoginModal: () => {
this.setState({loginModalOpen: !this.state.loginModalOpen});
},
toggleSignupModal: () => {
this.setState({signupModalOpen: !this.state.signupModalOpen});
},
toggleNavbar: () => {
this.setState({navbarOpen: !this.state.navbarOpen})
},
toggleTab: (tab) => {
if(this.state.activeTab !== tab) {
this.setState({activeTab: tab});
}
}
}
render() {
return (
<Context.Provider value={this.state}>
{this.props.children}
</Context.Provider>
)
}
}
also since you can't set state in render directly, you would need to create a component that handles this case
class Handler extends React.Component {
componentDidMount() {
this.props.setActiveTab(this.props.activeTab);
}
render() {
return null;
}
}
and your Router config would look like
<BrowserRouter>
<div>
<Route exact path='/' render={() => <Redirect to='/home/' /> } />
<Route exact path='/home/' render={() => <Handler setActiveTab={setActiveTab} activeTab={'1'} />}/>
<Route exact path='/popular/' render={() => <Handler setActiveTab={setActiveTab} activeTab={'2'} />}/>
<Route exact path='/all/' render={() => <Handler setActiveTab={setActiveTab} activeTab={'3'} />}/>
</div>
</BrowserRouter>

Resources