The correct login page pattern - javascript

I am sorry for my stupid question but i am really new in react and this problem make me stuck for days. I am kinda confused to make a login page in reactjs. my app.js code is like this :
import React from 'react';
import {HashRouter as Router, Route} from 'react-router-dom';
import asyncComponent from './AsyncComponent';
import AppShell from './AppShell';
import Login from './login/Login';
const Dashboard = asyncComponent(() => {
return import(/* webpackChunkName: "dashboard" */ './dashboard/Dashboard')
.then(module => module.default);
});
const LoginPage = asyncComponent(() => {
return import(/* webpackChunkName: "login" */ './login/Login')
.then(module => module.default);
});
class App extends React.Component {
render() {
return (
<Router>
<AppShell>
<div>
<Route exact path="/" component={Dashboard} />
<Route path="/login" component={LoginPage} />
</div>
</AppShell>
</Router>
);
}
}
export default App;
And this is my AppShell code :
import React, {Component} from 'react';
import {Link} from 'react-router-dom';
import {MuiThemeProvider} from 'material-ui/styles';
import {AppBar, Drawer, MenuItem} from 'material-ui';
import {DashboardIcon} from './icon/Icons';
import ArrowDropRight from 'material-ui/svg-icons/navigation-arrow-drop-right';
const ContentStyle = {
width: '90%',
margin: 'auto',
marginTop: '30px'
};
class SidebarDrawer extends React.Component {
componentDidMount() {
let frameCount = 0;
const open = () => (frameCount++ > 0) ? this.props.onMounted() :
requestAnimationFrame(open);
requestAnimationFrame(open);
}
render() {
return (
<Drawer
docked={false}
width={200}
open={this.props.open}
onRequestChange={this.props.onRequestChange}
>
<MenuItem
primaryText={'Dashboard'}
leftIcon={<DashboardIcon/>}
containerElement={<Link to={'/'}/>}
onClick={this.props.onClick}
/>
</Drawer>
);
}
}
class AppShell extends Component {
constructor(props) {
super(props);
this.state = {
open: false,
drawer : false
};
}
handleDrawerToggle = (e) => {
if (!this.state.drawer) {
this.setState({drawer: true});
e.preventDefault();
} else {
this.setState({open: !this.state.open});
}
}
render() {
const LazySidebarDrawer = this.state.drawer && (<SidebarDrawer
open={this.state.open}
onMounted={() => this.setState({open: true})}
onClick={() => this.setState({open: false})}
onRequestChange={open => this.setState({open: open})}
/>)
return (
<MuiThemeProvider>
<div>
<AppBar
title="Dashboard"
iconClassNameRight="muidocs-icon-navigation-expand-more"
onLeftIconButtonTouchTap={this.handleDrawerToggle}
/>
{LazySidebarDrawer}
<div id="content" style={ContentStyle}>
{React.cloneElement(this.props.children)}
</div>
</div>
</MuiThemeProvider>
);
}
};
export default AppShell;
But i still can access dashboard when i open login page. How is the correct pattern for login page?
Thanks

Your routing is correct, the exact '/' will only render the Dashboard component when the path is '/'. What you're seeing is the dashboard drawer or AppBar component. The dashboard drawer is still there in the login screen because it's always there in the AppShell code and your routes are children of AppShell. A potential solution would be to move that AppBar component to your Dashboard component if you only want it there.

Related

Warning: Can't perform a React state update on an unmounted component when login with auth0

I am following a tutorial from https://auth0.com/blog/role-based-access-control-rbac-and-react-apps/ and it seems that author doesn't support it anymore.
The idea is simple: Once a user presses Login button (on Header.js) he is redirected to auth0 page. There he enters his data and is redirected back to localhost:3000/callback route. This is when handleAuthentication is triggered.
Unfortunately, I am facing an issue when setting the state when the setSession function is used.
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.
in Auth (created by App)
in App
Here are the components:
App.js
import React from 'react'
import { Switch, Route, BrowserRouter as Router } from 'react-router-dom'
import Auth from './Auth';
import CallbackPage from "../pages/callback";
class App extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<Auth>
<div style={{width: '1280px', margin: '0 auto'}}>
<Router>
<Switch>
<Route exact path='/' component={HomePage} />
<Route path='/callback' component={CallbackPage} />
</Switch>
</Router>
</div>
</Auth>
)
}
}
export default App;
Auth.js
import React, {Component} from 'react';
import auth0 from 'auth0-js';
import {AUTH_CONFIG} from '../auth0-variables';
import {AuthProvider} from '../authContext';
const auth = new auth0.WebAuth({
domain: AUTH_CONFIG.domain,
clientID: AUTH_CONFIG.clientId,
redirectUri: AUTH_CONFIG.callbackUrl,
audience: `https://${AUTH_CONFIG.domain}/userinfo`,
responseType: 'token id_token'
});
class Auth extends Component {
state = {
authenticated: false,
}
initiateLogin = () => {
auth.authorize();
};
handleAuthentication = () => {
auth.parseHash((error, authResult) => {
if (error) {
console.log(error);
console.log(`Error ${error.error} occured`);
return
}
this.setSession(authResult);
})
};
setSession(authResult) {
this.setState({
// This does not update the state!!
authenticated: true,
});
};
render() {
const authProviderValue = {
...this.state,
initiateLogin: this.initiateLogin,
handleAuthentication: this.handleAuthentication,
};
return (
<AuthProvider value={authProviderValue}>
{this.props.children}
</AuthProvider>
)
}
};
export default Auth;
Header.js (Can component can be found at https://github.com/itaditya/react-rbac-auth0-article-code/blob/master/src/components/Can.js)
import React, { useEffect, useReducer } from 'react';
import {
BrowserRouter as Router,
Link }
from 'react-router-dom';
import Login from '../Login';
import Logout from '../Logout';
import Can from '../Can';
import { AuthConsumer } from '../../authContext';
const Header = (props) => {
return (
<AuthConsumer>
{({ user }) => (
<Can role={user.role} perform='home-page:seeLogin'
yes={() => (
<Login />
)}
no={() => (
<Logout />
)}
/>
</AuthConsumer>
)
}
export default Header
And pages:
homePage:
import React from 'react';
const HomePage = () => {
return (
<div>
<Header />
</div>
)
};
export default HomePage;
Callback page
import React from 'react';
import { Redirect } from 'react-router-dom';
import { AuthConsumer } from '../authContext';
const Callback = props => (
<AuthConsumer>
{({ handleAuthentication }) => {
if (/access_token|id_token|error/.test(props.location.hash)) {
handleAuthentication();
}
return <Redirect to='/' />;
}}
</AuthConsumer>
);
export default Callback;
Any help would be appreciated.

React refresh component on login

I have 2 components, NavBar which contains a login modal and the 'body' of page.
I want to detect when a user logs in and re-render the page based on that. How do I update the login prop in the second component when I log in using the modal of the first one?
A simplified version of the code to keep it short:
// NavBar.js
export default class NavBar extends Component {
constructor(props) {
super(props)
this.initialState = {
username: "",
password: "",
loginModal: false
}
this.handleLogin = this.handleLogin.bind(this)
}
handleLogin(e) {
e.preventDefault()
loginAPI.then(result)
}
render() {
return( <nav> nav bar with links and login button </nav>)
}
// Some random page
export default class Checkout extends Component {
constructor(props) {
super(props);
this.state = {
order_type: 'none',
loggedIn: false
}
this.Auth = new AuthService()
}
componentDidMount() {
if (this.Auth.loggedIn()) {
const { username, email } = this.Auth.getProfile()
this.setState({ loggedIn: true, email: email })
}
try {
const { order_type } = this.props.location.state[0]
if (order_type) {
this.setState({ order_type: order_type })
}
} catch (error) {
console.log('No package selected')
}
}
componentDidUpdate(prevProps, prevState) {
console.log("this.props, prevState)
if (this.props.loggedIn !== prevProps.loggedIn) {
console.log('foo bar')
}
}
render() {
return (
<section id='checkout'>
User is {this.state.loggedIn ? 'Looged in' : 'logged out'}
</section>
)
}
}
// App.js
function App() {
return (
<div>
<NavBar />
<Routes /> // This contains routes.js
<Footer />
</div>
);
}
// routes.js
const Routes = () => (
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/register" component={Register} />
<Route exact path="/registersuccess" component={RegisterSuccess} />
<Route exact path="/faq" component={FAQ} />
<Route exact path="/checkout" component={Checkout} />
<Route exact path="/contact" component={Contact} />
{/* <PrivateRoute exact path="/dashboard" component={Dashboard} /> */}
<Route path="/(notfound|[\s\S]*)/" component={NotFound} />
</Switch>
)
I would recommend using the react context API to store information about the logged in user.
See: https://reactjs.org/docs/context.html
Example
auth-context.js
import React from 'react'
const AuthContext = React.createContext(null);
export default AuthContext
index.js
import React, { useState } from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import AuthContext from './auth-context.js'
const AppWrapper = () => {
const [loggedIn, setLoggedIn] = useState(false)
return (
<AuthContext.Provider value={{ loggedIn, setLoggedIn }}>
<App />
</AuthContext.Provider>
)
}
ReactDOM.render(
<AppWrapper/>,
document.querySelector('#app')
)
Then inside any component you can import the AuthContext and use the Consumer component to check if the user is logged in order set the logged in state.
NavBar.js
import React from 'react'
import AuthContext from './auth-context.js'
const NavBar = () => (
<AuthContext.Consumer>
{({ loggedIn, setLoggedIn }) => (
<>
<h1>{loggedIn ? 'Welcome' : 'Log in'}</h1>
{!loggedIn && (
<button onClick={() => setLoggedIn(true)}>Login</button>
)}
</>
)}
</AuthContext.Consumer>
)
export default NavBar
HOC version
with-auth-props.js
import React from 'react'
import AuthContext from './auth-context'
const withAuthProps = (Component) => {
return (props) => (
<AuthContext.Consumer>
{({ loggedIn, setLoggedIn }) => (
<Component
loggedIn={loggedIn}
setLoggedIn={setLoggedIn}
{...props}
/>
)}
</AuthContext.Consumer>
)
}
export default withAuthProps
TestComponent.js
import React from 'react'
import withAuthProps from './with-auth-props'
const TestComponent = ({ loggedIn, setLoggedIn }) => (
<div>
<h1>{loggedIn ? 'Welcome' : 'Log in'}</h1>
{!loggedIn && (
<button onClick={() => setLoggedIn(true)}>Login</button>
)}
</div>
)
export default withAuthProps(TestComponent)
Alternatively if you have redux setup with react-redux then it will use the context API behind the scenes. So you can use the connect HOC to wrap map the logged in state to any component props.

React ReactCSSTransitionGroup with asyncComponent

I'm trying to load component asynchronicity with appropriate component base on the ReactCSSTransitionGroup package by this example: https://codesandbox.io/s/zkqlq2vo?from-embed
So I combined all to one component as the following:
import React, { Component } from 'react';
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
import './PageShell.less';
const PageShell = (importComponent) => {
return class extends Component {
state = {
component: null
}
componentDidMount() {
importComponent()
.then(cmd => {
this.setState({ component: cmd.default });
})
.catch({});
}
render() {
const C = this.state.component;
const component = C ? (<C {...this.props} />) : null;
return (
<ReactCSSTransitionGroup
transitionAppear={true}
transitionAppearTimeout={600}
transitionEnterTimeout={600}
transitionLeaveTimeout={200}
transitionName={`Slide${Math.random() >= 0.5 ? 'In' : 'Out'}`}>
{component}
</ReactCSSTransitionGroup>
);
}
};
};
export default PageShell;
And on my App.js:
import React, { Component } from 'react';
import Layout from './hoc/Layout/Layout';
import BurgerBuilder from './containers/BurgerBuilder/BurgerBuilder';
import Logout from './containers/Auth/Logout/Logout';
import AsyncComponent from './hoc/AsyncComponent/AsyncComponent';
const asyncCheckout = AsyncComponent(() => {
return import('./containers/Checkout/Checkout/Checkout');
});
const asyncOrders = AsyncComponent(() => {
return import('./containers/Orders/Orders');
});
const asyncAuth = AsyncComponent(() => {
return import('./containers/Auth/Auth/Auth');
});
class App extends Component {
render() {
let routes = (
<Switch>
<Route path="/auth" component={PageShell(asyncAuth)} />
<Route path="/" exact component={PageShell(BurgerBuilder)} />
<Redirect to="/" />
</Switch>
);
return (
<div>
<Layout>
{routes}
</Layout>
</div>
);
}
}
export default App;
For some reason that i don't understand, the transition in my upgraded PageShell component not working, like in the example from codesandbox, and i can't figure out why is that.

Redirect user to a Login Route on start of the application in react-redux

I am new to the react-redux. Here, what I want to do is that, when user hits the url lets say , localhost:3000 then user should directly move to the src/index.js localhost:3000/login page . And If user knows some routes and hits them without login then also it should redirect it to the
login page.
for that , what I have tried,
**Home.js**
import React from 'react';
import { Route, Switch } from 'react-router-dom';
import App from './App';
import LoginComponent from './Components/loginComponent/LoginComponent';
class Home extends React.Component {
render() {
const rootPath = "/";
return (
<Switch>
<Route path={rootPath} component={App}/>
</Switch>
)
}
}
export default Home
**App.js**
import React from 'react';
import './App.css';
import { Provider } from 'react-redux';
import Main from './Containers/Main/Main';
import configureStore from './AppStore'
const store = configureStore()
class App extends React.Component {
render() {
return (
<Provider store={store}>
<div className="container-fluid">
<Main />
</div>
</Provider>
)
}
}
export default App
**Main.js**
import React from 'react';
import { Route, Redirect } from 'react-router';
import LoginComponent from '../../Components/loginComponent/LoginComponent';
import { LOGIN_REDIRECT_URL } from './../../Constants/AppConstants';
export default class Main extends React.Component {
constructor(props) {
super(props);
this.state = {
error: false,
hasUserLogedIn: false
}
}
render() {
const template =
!this.props.hasUserLogedIn
? (
<Route path="/*" render={(props) => <LoginComponent />}/>
) : (
<span>After login </span>
)
return (
<div>
{template}
</div>
)
}
}
function mapStateToProps(state) {
}
So, In the last file, I am doing that redirection, but it is not working. can any one help me with this ?
Because of the /* it is redirecting user to same view.
You can use public and private routes:
const PrivateRoute = ({ component: Component, ...rest, loggedIn }) => (
<Route
{...rest}
render={props =>
(loggedIn ? (
<Component {...props} />
) : (
<Redirect
to={{
pathname: LOGIN,
state: { from: props.location },
}}
/>
))
}
/>
);
const PublicRoute = ({ component: Component, ...rest, loggedIn}) => (
<Route
{...rest}
render={props =>
(!loggedIn ? (
<Component {...props} />
) : (
<Redirect
to={{
pathname: HOME,
state: { from: props.location },
}}
/>
))
}
/>
)

How to pass the state of the page to other React?

I want to know how I can pass a status from one page to another page for if used in the other way.
My first page Body.js (Which I handle the state):
import React from 'react';
import './Body.css';
import axios from 'axios';
import { Link } from "react-router-dom";
import User from './User';
class Body extends React.Component {
constructor (){
super();
this.state ={
employee:[],
employeeCurrent:[],
}
}
componentDidMount(){
axios.get('http://127.0.0.1:3004/employee').then(
response=>this.setState({employee: response.data})
)
}
getName = () => {
const {employee} = this.state;
return employee.map(name=> <Link className='link' to={`/user/${name.name}`}> <div onClick={()=>this.add(name)} key={name.id} className='item'> <img className='img' src={`https://picsum.photos/${name.name}`}></img> <h1 className='name'> {name.name} </h1></div> </Link>)
}
add = (name) => {
const nam = name;
this.state.employeeCurrent.push(nam)
console.log(this.state.employeeCurrent)
}
render(){
return(
<div className='body'>
{this.getName()}
</div>
)
}
}
export default Body;
My second page which I want to get the state called employeeCurrent:
import React from 'react';
import Header from './Header';
import Body from './Body';
class User extends React.Component {
constructor (props){
super(props);
this.props ={
employeeCurrent:[],
}
}
render(){
return(
<div >
{this.props.employeeCurrent}
</div>
)
}
}
export default User;
I'm using the React Router, it looks like this:
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import './App.css';
import Home from './Home';
import User from './User';
const AppRouter = () => (
<Router>
<div className='router'>
<Route exact path="/" component={Home}/>
<Route path="/user/:id" component={User}/>
</div>
</Router>
);
export default AppRouter;
My project is:
Home page, where you have users, obtained from the API, all users have attributes (name, age, city and country). Saved in employeeCurrent variable:
What I want is: grab these attributes from the clicked user and play on the user page:
Someone would can help me PLEASE?????
Like I explained earlier, you need to lift the state up:
AppRouter (holds the state and passes it to children)
class AppRouter extends React.Component {
state = {
employeeCurrent: [],
employee: []
};
componentDidMount() {
axios
.get("http://127.0.0.1:3004/employee")
.then(response => this.setState({ employee: response.data }));
}
add = name => {
this.setState(prevState => {
const copy = prevState.employeeCurrent.slice();
copy.push(name);
return {
employeeCurrent: copy
};
});
};
render() {
return (
<Router>
<div className="router">
<Route
exact
path="/"
render={props => (
<Home
{...props}
add={this.add}
employee={this.state.employee}
currentEmployee={this.state.currentEmployee}
/>
)}
/>
<Route
path="/user/:id"
component={props => (
<User
{...props}
employee={this.state.employee}
currentEmployee={this.state.currentEmployee}
/>
)}
/>
</div>
</Router>
);
}
}
Body and User (receive parent state as props together with updater functions):
class Body extends React.Component {
getName = () => {
const { employee, add } = this.props;
return employee.map(name => (
<Link className="link" to={`/user/${name.name}`}>
{" "}
<div onClick={() => add(name)} key={name.id} className="item">
{" "}
<img
className="img"
src={`https://picsum.photos/${name.name}`}
/>{" "}
<h1 className="name"> {name.name} </h1>
</div>{" "}
</Link>
));
};
render() {
return <div className="body">{this.getName()}</div>;
}
}
class User extends React.Component {
render() {
// you will need to map employeeCurrent somehow
return <div>{this.props.employeeCurrent}</div>;
}
}

Categories

Resources