Take this.state to outer scope - javascript

Here's my App.js
import React, {Component} from 'react';
import logo from './logo.svg';
import './App.css';
import {BrowserRouter as Router} from 'react-router-dom';
import Navbar from "./components/nav";
import firebase from './firebase';
import Questions from './components/questions';
import Stuff from './components/stuff';
class App extends Component {
constructor(p) {
super(p);
this.state = {user: null}
}
render() {
return (
<div className="App">
<Navbar/>
{this.state.user ? <Questions/> : <Stuff/>}
</div>
);
}
}
export default App;
The idea is, it should render the navbar, and if the user is logged in, it should render Questions element, and otherwise, render something else.
Here's my <Navbar/> element:
import React from 'react';
import {Navbar as Bar, Nav, NavItem, Modal, Button, FormControl} from 'react-bootstrap';
import {BrowserRouter as Router, Link, Route} from 'react-router-dom';
import firebase, {auth} from '../firebase';
class Navbar extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.login = this.login.bind(this);
this.logout = this.logout.bind(this);
this.openLogin = this.openLogin.bind(this);
this.handleClose = this.handleClose.bind(this);
}
componentDidMount() {
auth.onAuthStateChanged((user) => {
if (user) {
this.setState({user});
}
});
}
logout() {
auth.signOut()
.then(() => {
this.setState({
user: null
});
});
}
login() {
var email = document.getElementById('email').value;
var password = document.getElementById('password').value;
auth.signInWithEmailAndPassword(email, password)
.then(result => {
const user = result.user;
this.setState({user});
document.getElementById('close').click();
}
).catch(e => console.log(e));
}
openLogin() {
this.setState({show: true});
}
handleClose() {
this.setState({show: false});
}
render() {
return (
<React.Fragment>
<Router>
<Bar>
<Bar.Header>
<Bar.Brand>
UczIchApp
{/*<Route path='path' component=""/>*/}
</Bar.Brand>
<Bar.Brand>
</Bar.Brand>
</Bar.Header>
<Nav>
{
this.state.user ?
<NavItem onClick={this.logout}>Wyloguj się</NavItem>
:
<NavItem onClick={this.openLogin}>Zaloguj się</NavItem>
}
</Nav>
</Bar>
</Router>
<Modal show={this.state.show} onHide={this.handleClose}>
<Modal.Header closeButton>
<Modal.Title>Modal heading</Modal.Title>
</Modal.Header>
<Modal.Body>
<form>
<FormControl
id="email"
type="email"
label="Email address"
placeholder="Enter email"
/>
<FormControl id="password" label="Password" type="password"/>
<Button onClick={this.login}>Zaloguj</Button>
</form>
</Modal.Body>
<Modal.Footer>
<Button id="close" onClick={this.handleClose}>Close</Button>
</Modal.Footer>
</Modal>
</React.Fragment>
)
}
}
export default Navbar;
So I want to change the interior in the <App/> element, based on the user state changed in the <Navbar/> element. How can I do that? Right now the state is unavailable in <App.js/>

Make a function checkUserState in your App Component
constructor(p) {
super(p);
this.state = {user: null}
this.checkUserState= this.checkUserState.bind(this);
}
checkUserState(user){
this.seState({user});
}
And then pass props in Navbar component as
<Navbar {...this}/>
Inside Navbar Component where you set state, do this
this.setState({
user: null
},function(){
this.props.checkUserState(this.state.user)
});
Since you will need to go reverse as Navbar is a child component.

It looks like you need to globally handle the state of the app. You could try using redux to manage the state of that app. or you can just pass a function as a prop to the nav from your parent component and that function can handle state
Js fiddle example
const Nav = ({ handleClick }) => {
return (<button onClick={handleClick}>hello world</button>);
}
class HelloMessage extends React.Component {
constructor(props){
super(props);
this.state = {
checked: false
}
}
handleClick = (e) => {
e.preventDefault();
this.setState({checked: !this.state.checked});
}
render() {
return (
<div>
<Nav handleClick={this.handleClick}/>
<p><b>status:</b> {this.state.checked ? 'checked' : 'unchecked'}</p>
</div>
);
}
}
ReactDOM.render(
<HelloMessage name="Taylor" />,
document.getElementById('app')
);

Related

oktaAuth with react stuck in login/callback

I am using
"#okta/okta-auth-js": "^5.8.0",
"#okta/okta-react": "^6.3.0",
in my react app.
This is my App.js:
import React, { Component } from 'react';
import { Router } from 'react-router-dom';
import AppWithRouterAccess from './AppWithRouterAccess';
import history from './data/history';
class App extends Component {
render() {
return (
<Router history={history}>
<AppWithRouterAccess />
</Router>
);
}
}
export default App;
(history is just export default createBrowserHistory();
AppWithRouterAccess.js :
import React, { Component } from 'react';
import { Route, withRouter, Switch } from 'react-router-dom';
import { Security, SecureRoute, LoginCallback } from '#okta/okta-react';
import { OktaAuth, toRelativeUrl } from '#okta/okta-auth-js';
import NavBar from './components/NavBar';
import './App.css'
import Home from './pages/Home';
import Enrichment from './pages/Enrichment';
import PrePublish from './pages/PrePublish';
import Published from './pages/Published';
import CheckPublication from './pages/CheckPublication';
import SignIn from './pages/SignIn';
import history from './data/history';
const OktaCred = require('./data/idc_cred.json')
export default withRouter(class AppWithRouterAccess extends Component {
constructor(props) {
super(props);
this.state={
is_logged: false
}
this.onAuthRequired = this.onAuthRequired.bind(this);
this.oktaAuth = new OktaAuth({
clientId: OktaCred.clientId,
issuer: OktaCred.issuer,
redirectUri: OktaCred.redirectUri,
scopes: ['openid', 'profile', 'email'],
postLogoutRedirectUr: OktaCred.postLogoutRedirectUri
});
}
onAuthRequired = () => {
history.push('/login');
}
restoreOriginalUri = async(_oktaAuth, originalUri) => {
history.replace(toRelativeUrl(originalUri, window.location.origin));
};
render() {
return (
<>
{this.state.is_logged?
<NavBar/>:undefined}
<Security oktaAuth={this.oktaAuth} restoreOriginalUri={this.restoreOriginalUri}>
<Switch>
<SecureRoute path='/' exact component={Home}/>
<Route path='/login' render={()=><SignIn />} />
<Route path='/login/callback' component={LoginCallback} />
</Switch>
</Security>
</>
);
}
});
idc_cred.json is a JSON file with all the Okta credentials (tested and works!)
SignIn.jsx is:
import React, { Component } from 'react';
import { Redirect } from 'react-router-dom';
import SignInForm from '../components/SignInForm.jsx';
import { withOktaAuth } from '#okta/okta-react';
export default withOktaAuth(class SignIn extends Component {
render() {
if (!this.props.authState) {
return <div>Loading...</div>;
}
return this.props.oktaAuth.isAuthenticated ?
<Redirect to="/"/> :
<SignInForm />;
}
});
and finally, SignInForm.jsx is:
import React, {Component} from "react";
import './styles/SignInForm.css';
import { withOktaAuth } from '#okta/okta-react'
import Logo from '../assets/Jfrog-Logo.svg'
export default withOktaAuth( class SignInForm extends Component{
constructor(props){
super(props);
this.state={
sessionToken: null,
username: '',
password: '',
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleUsernameChange = this.handleUsernameChange.bind(this);
this.handlePasswordChange = this.handlePasswordChange.bind(this);
}
handleSubmit = async (e) => {
e.preventDefault();
console.log(this.state)
this.props.oktaAuth.signInWithCredentials({
username: this.state.username,
password: this.state.password
})
.then(transaction => {
var pushFactor = transaction.factors.find(function(factor) {
return factor.provider === 'OKTA' && factor.factorType === 'push';
});
pushFactor.verify({
autoPush: true
})
console.log(transaction)
if(transaction.sessionToken){
this.setState(
{ sessionToken: transaction.sessionToken },
() => {
this.props.oktaAuth.signInWithRedicred({sessionToken: transaction.sessionToken})
}
)
}
})
.catch(err => console.log('Found an error', err));
}
handleUsernameChange = (e) => {
this.setState({username: e.target.value});
}
handlePasswordChange = (e) => {
this.setState({password: e.target.value});
}
render(){
if (this.state.sessionToken) {
return null;
}
return(
<div className="login-container">
<div className="total-login">
<div className="logo-login">
<img src={Logo} alt="logo" className="logo-self-login"/>
</div>
<div className="login-form-main">
<form onSubmit={this.handleSubmit}>
<p className="login-title">Login with Okta</p>
<label>
<p className="login-sub-title">Username:</p>
<input className="login-input" id="username" type="text" value={this.state.username} onChange={this.handleUsernameChange}/>
</label>
<label>
<p className="login-sub-title">Password:</p>
<input id="password" type="password" className="login-input" value={this.state.password} onChange={this.handlePasswordChange}/>
</label>
<br/>
<button className="login-button" type="submit" id="submit" value="Submit">Login</button>
<div/>
</form>
</div>
</div>
</div>
);
}
})
When the user enters his creds, the page becomes a blank page with Loading..., like so:
What am I missing?
Thanks!
The network tab:

On successful login, this.props.history.push does not take to new page as ProtectedRoute is not successful

After I receive a 200 status code, I try to go to the dashboard page. However, it does not work as intended. I am using React Context and ProtectedRoute. When I get the successful response, I call the login() function and it makes isAuthenticated as true but still does not take me to the dashboard Page.
PS: I checked and saw that in my protected route, it goes to the second part and gets redirected to "/" and hence I am on the same page. Do not know why that is happening
Signin
import { AuthContext } from './AuthContext'
async handleSubmit(event) {
//I send the request before this
const status = await res.status
const json = await res.json();
if(status === 200) {
const {login} = this.context;
login();
this.props.history.push('/dashBoard')
console.log('done')
} else {
console.log('not done')
}
} catch (error) {
console.log('Error', error)
}
}
static contextType = AuthContext;
render() {
return (
<Container className="formlogin">
<div>
<Form className = "signinform backg-sheet1" onSubmit={this.handleSubmit}>
<div>
<h3 style = {{color: 'white', textAlign: 'center'}}> SIGN IN </h3>
<Form.Group controlId = "formBasicUsername">
<Form.Label style = {{color: 'white'}}>Username</Form.Label>
<Form.Control required name = "username" type = "text" placeholder = "Enter username" onChange = {this.handleChange}/>
</Form.Group>
<Form.Group controlId = "formBasicPassword">
<Form.Label style = {{color: 'white'}}>Password</Form.Label>
<Form.Control required name = "password" type = "password" placeholder = "Enter password" onChange = {this.handleChange}/>
</Form.Group>
<div style={{textAlign: 'center'}}>
<Button variant="warning" type="submit">
Submit
</Button>
</div>
</div>
</Form>
</div>
</Container>
);
}
}
export default withRouter(SignIn);
App.js
class App extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<Router>
<AuthProvider>
<NavigationBar />
<Switch>
<Route exact path = '/'
component = {LandingPage}
/>
<Route exact path = '/register'
component = {Register}
/>
<ProtectedRoute exact path = '/dashBoard'
component = {dashBoard}
/>
</Switch>
</AuthProvider>
</Router>
</div>
);
}
}
export default App;
AuthProvider
import React, { Component} from 'react';
export const AuthContext = React.createContext();
class AuthProvider extends Component {
state = {
isAuthenticated: false
}
constructor() {
super()
this.login = this.login.bind(this)
this.logout = this.logout.bind(this)
}
login() {
setTimeout(() => this.setState({ isAuthenticated: true }), 1000)
}
logout() {
this.setState({ isAuthenticated: false })
}
render() {
console.log(this.state.isAuthenticated)
return(
<AuthContext.Provider value={{isAuthenticated: this.state.isAuthenticated, login: this.login, logout: this.logout}}>
{this.props.children}
</AuthContext.Provider>
)
}
}
const AuthConsumer = AuthContext.Consumer
export { AuthProvider, AuthConsumer }
ProtectedRoute
import React from 'react'
import { Route, Redirect } from 'react-router-dom'
import { AuthConsumer } from './AuthContext'
const ProtectedRoute = ({component: Component, ...rest}) => (
<AuthConsumer>
{({ isAuthenticated }) => (
<Route
render={(props) =>
isAuthenticated?<Component {...props} /> : <Redirect to="/" />
}
{...rest}
/>
)}
</AuthConsumer>
)
export default ProtectedRoute
LandingPage
import React from 'react';
import SignIn from './SignIn'
import NavigationBar from './NavigationBar'
import {Row} from 'react-bootstrap';
import './Styles.css'
class LandingPage extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<div className = "backg">
<div className = "backg-sheet">
<SignIn />
</div>
</div>
</div>
)
}
}
export default LandingPage
Make few corrections and you will successfully get navigated to dashboard page.
Your handleSubmit is a normal function and you are losing the context of this. So make it an arrow function
Use event.preventDefault else browser will reload and you end up back to / page.
I have placed your code in the sandbox and made it work.
Code snippet
handleSubmit = async event => {
try {
event.preventDefault();
//I send the request before this
// console.log("this.context", this.context); //<---- prints successfully
const status = 200; //await res.status;
// const json = await res.json();
if (status === 200) {
const { login } = this.context;
await login();
console.log("this.context", this.context);
this.props.history.push("/dashBoard");
console.log("done");
} else {
console.log("not done");
}
} catch (error) {
console.log("Error", error);
}
};
You need to implement withRouter component in order for you have access with this.props.hostory... variable just like this below.
import { withRouter } from 'react-router';
export default withRouter(Login);

React router: component not rendering after login

I'm building a website using React and I'm trying to redirect the user to the index page after the login, but my component is not rendering anything, although I'm being redirected and I can see the URL changing from /welcome#/login to /main.
Since I'm not getting any error messages and the webpack is being successfully compiled, I can't see what's wrong anymore.
Any ideas of what could possibly be wrong?
Thank you very much!
Start.js
import React from "react";
import ReactDOM from "react-dom";
import Welcome from "./welcome";
import { App } from "./app";
import { Provider } from "react-redux";
import { createStore, applyMiddleware } from "redux";
import reduxPromise from "redux-promise";
import { composeWithDevTools } from "redux-devtools-extension";
import reducer from "./reducer";
const store = createStore(
reducer,
composeWithDevTools(applyMiddleware(reduxPromise))
);
let element;
if (location.pathname === "/welcome") {
element = <Welcome />;
} else {
init(store);
element = (
<Provider store={store}>
<App />
</Provider>
);
}
ReactDOM.render(element, document.querySelector("main"));
Welcome.js
import React from "react";
import Register from "./register";
import Login from "./login";
import { HashRouter, Route, Redirect } from "react-router-dom";
export default class Welcome extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<HashRouter>
<div className="register-wrapper">
<div>
<Route path="/login" component={Login} />
<Route exact path="/" component={Register} />
</div>
</div>
</HashRouter>
);
}
}
Login Component
import React from "react";
import axios from "./axios";
export default class Login extends React.Component {
constructor(props) {
super(props);
this.state = { error: false };
this.loginButton = this.loginButton.bind(this);
this.handleChange = this.handleChange.bind(this);
}
loginButton(e) {
e.preventDefault();
axios
.post("/login", this.state)
.then(res => {
if (res.data.success) {
location.replace("/main");
} else {
this.setState({
error: true
});
}
})
.catch(error => console.log(error));
}
handleChange(e) {
this.setState(
{
[e.target.name]: e.target.value
},
() => console.log("this.state:", this.state)
);
}
render() {
return (
<div className="login-main-container">
{this.state.error && <h2>Ops! Something went wrong.</h2>}
<h1 className="login-title">Kathi & Rodolfo</h1>
<h2 className="login-subtitle">Dear guest, please login first</h2>
<div className="login-container">
<form className="login-form">
<label className="label-login" htmlFor="email"> username </label>
<input className="input-login"
name="email"
placeholder="Best Couple You Know"
onChange={this.handleChange}
/>
<label className="label-login" htmlFor="password"> password </label>
<input className="input-login"
name="password"
type="password"
placeholder="Super Loving Password"
onChange={this.handleChange}
/>
<button
className="login-button"
onClick={this.loginButton}
>
Login
</button>
</form>
</div>
<h4 className="login-info">Information about username and password can be found on the Save The Date card</h4>
</div>
);
}
}
Index Component (Main)
import React from "react";
export default class Main extends React.Component {
constructor(props) {
super(props);
this.state ={ error: false};
}
render () {
return (
<div className="main-container">
<header>
<p>the wedding</p>
<p>rpsv</p>
<p>contact us</p>
<p>wedding gift</p>
</header>
{this.state.error && <h2>Ops! Something went wrong.</h2>}
<div className="save-the-date-img">
<h1>Save The Date</h1>
</div>
</div>
);
}
}
App.js
import React from "react";
import { BrowserRouter, Route, Link } from "react-router-dom";
import Main from "./main";
export class App extends React.Component {
constructor() {
super();
this.state = {};
}
render() {
return (
<BrowserRouter>
<div>
<Route exact path="/main" Component={Main}/>
</div>
</BrowserRouter>
);
}
}
location.replace("/main");
I think this line is wrong in your code.
While you are using React, you'd rather use react-router-dom's functionality than browser built-in feature.
Change the line to this.props.history.push('/main')
Give your file structure. Your component Login is not in BrowserRouter, but must be. Check the official sample: https://reacttraining.com/react-router/web/guides/quick-start.
instead of
location.replace("/main");
use
history.push("/main")

The correct login page pattern

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.

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