Form dialog don't set state - javascript

I have created a simple login function where once the user log in, he is redirect to another page. Then I wanted to change the login form with a form dialog. And the problem is here. The login dialog works, but when I enter the username and password, I'm not send to another page but to the same login page :/.
Here is the code:
Login.jsx:
class Login extends Component {
constructor(props) {
super(props);
this.state = {
islogged: false,
loginSettings: {
lUsername: "",
lPassword: ""
}
};
}
handleInput = (event) => {
let loginSettingsNew = { ...this.state.loginSettings };
let val = event.target.value;
loginSettingsNew[event.target.name] = val;
this.setState({
loginSettings: loginSettingsNew
});
};
login = (event) => {
let lUsername = this.state.loginSettings.lUsername;
let lPassword = this.state.loginSettings.lPassword;
if (lUsername === "admin" && lPassword === "password") {
localStorage.setItem("token", "T");
this.setState({
islogged: true
});
} else {
console.log("Erreur");
}
event.preventDefault();
};
render() {
if (localStorage.getItem("token")) {
return <Redirect to="/" />;
}
return (
<div className="Login">
<Dialog handleInput={this.handleInput} login={this.login} />
<p>Username: admin - Password: password</p>
</div>
);
}
}
Dialog.js:
export default function FormDialog() {
const [open, setOpen] = React.useState(false);
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return (
<div>
<Button variant="outlined" color="primary" onClick={handleClickOpen}>
Open form dialog
</Button>
<Dialog open={open} onClose={handleClose}>
<DialogTitle>Login</DialogTitle>
<DialogContent>
<form onSubmit={this.login}>
<label>
<span>Username</span>
<input name="lUsername" type="text" onChange={this.handleInput} />
</label>
<label>
<span>Password</span>
<input name="lPassword" type="password" onChange={this.handleInput}/>
</label>
<Button onClick={handleClose} color="primary">Cancel</Button>
<Button type="submit" value="submit" color="primary">Login</Button>
</form>
</DialogContent>
</Dialog>
</div>
);
}
I also have created a sandbox of my code: https://codesandbox.io/s/react-login-auth-forked-r06ht
I thinks that the problem come from the form dialog that can't set the state of islogged of the login function. I tried quite a lot of things but nothing worked, so I would like to ask some help please.
I thank in advance anyone who will take the time to help me .

I notice that your Login component is a class, while the child FormDialog component is a function component using Hooks. There's nothing wrong with that, in itself - particularly if you started with an application using all class components and are slowly converting your components. But I detect some confusion here, because your function component seems to assume it's a class, in a sense. When you need to do things a bit differently.
Specifically, you reference this.login in your function component - but this is meaningless. login is passed in as a prop. In a class component, you would reference it as this.props.login. This doesn't work in a function component - but this.login isn't the substitute. Indeed this will tend to be undefined so this would even give an error. Even if not, it's not correct.
All you need to do is to make use of the argument to your function component - this is where the props object "lives" in a function component. You happen to ignore it by not using any arguments - but you don't have to do this.
So in short, what you need to do is:
replace export default function FormDialog() with export default function FormDialog(props)
replace this.login with props.login
repeat 2) for all other props which you have referenced using this

Ok all fixed. In your App.js you didn't have a home component to redirect to with this path="/". I've created a new component called home.js. Tidied the Routes for you.
<Route path="/login" component={Login} />
<ProtectedRoute path="/dashboard" component={Dashboard} />
<Route exact path="/" component={Home} />
Please check ur code sandbox https://codesandbox.io/s/react-login-auth-forked-24m8e?file=/src/App.js

Related

React with Typescript and React Router(v6)- Form's event.preventDefault() - seems to not work

I am having this weird issue when I press the button the submit refreshes the page and attaches to the browsers URL parameters. Seems that preventDefault() not getting called.
The function that triggers onSubmit is this one:
const handleLogin = (e: FormEvent<HTMLFormElement>) => {
console.log("triggered?")
e.preventDefault()
...some api calls
}
At first when I press the button (input[submit]) seems that this function is not getting called at all as it gets refreshed. But after this first call it attaches to the URL the form input fields and their values (which is quite bad because it exposes f.ex password to the URL).
the jsx of form looks like this:
<form onSubmit={handleLogin}>
...input elements
...
<div className={'form-group'}>
<input className="form-control"
value={"Login"}
name="login" type="submit"/>
</div>
</form>
EDIT:
So i've noticed that this issue happens because of react-router (v6);
<Routes>
<Route path="/" element={<Navigate to="ui"} />
<Route path="ui">
<Route index element={getRedirect()}/>
<Route path="login" element={<AuthPage/>}/>
</Route>
</Routes>
Because the moment the page loads react router makes "redirection" to /ui/ - localhost:port/ui/
And thats why on first click e.preventDefault() or e.stopPagination() doesnt'work . But if i refresh or go straight to localhost:port/ui/ instead of just localhost:port . The form will work as it should. Honestly i couldn't find a good solution for this :\
Currently i added something like this for dev env (in prod BE should redirect to /ui/ ) .
if (!process.env.NODE_ENV || process.env.NODE_ENV === 'development') {
if (!window.location.href.includes("/ui/")) {
window.location.href = window.location.href + "ui/"
}
But this looks very dodgy to me.
I think you are making some mistake here is the totally working example that's work perfectly fine.
const App = () => {
const [value, setValue] = useState("");
const handleChange = (event) => {
console.log(event.target.value);
setValue(event.target.value);
};
const handleSubmit = (event) => {
event.preventDefault();
console.log(value);
};
return (
<form onSubmit={handleSubmit}>
<label>
Name:
<input type="text" value={value} onChange={handleChange} />
</label>
<input type="submit" value="Login" name="login" />
</form>
);
};
export default App;

Is it possible to send the state to the page destination at the time of browser back with react-router-dom?

I'm using react.js and react-router-dom to create two pages. Form.js is the page where you enter your name in the form, and Confirmation.js is the page where you confirm the name.
I want to share the state of two classes. So, when you jump to another page from the link button, you will send the state at the same time. The sent state is received in the class constructor as this.state = props.history.location.state.
Many have omitted this code.
//Form.js
import React, { Component } from 'react'
import {Link} from 'react-router-dom'
class Form extends Component {
constructor(props) {
super(props);
const histState = props.history.location.state
this.state = histState == undefined ? {name: this.state} : histState
}
render() {
return (
<div>
<input type="text" onChange={this.handleFormInputChanged} value={this.state.name}/>
<Link to={pathname: "/confirmation" , state: this.state}>Send</Link>
</div>
)
}
}
//Confirmation.js
class Confirmation extends Component {
constructor(props) {
super(props);
this.state = props.history.location.state
}
render() {
return (
<div>
<div>Your Name : <span className="name">{this.state.name}</span></div>
<Link to={pathname: "/form" , state: this.state}>Edit</Link>
</div>
)
}
}
Now I can do what I want to do. However, I noticed that when the user pressed the browser back button on the Confirmation.js page, the state was not sent because it jumped to the Form.js page without pressing the Link component.
As a solution, I added the following code to Confirmation.js.
//Confirmation.js
componentWillUnmount() {
this.props.history.push("/form", this.state)
}
However, when I do a browser back this way and receive a state in the class constructor, props.history.location.state is undefined. And strangely, after a while or reloading, props.history.location.state is set to state normally.
//Form.js
constructor(props) {
...
console.log("Form constructor", props.history.location.state)
}
I want to resolve the time it takes for state to be set as the value of props.history.location.state, is there a solution?
You can pass basic parameters as route segments, like /form/:username, or you could use a query parameter like /form?username=Hiroaki, but passing around data more structured or complex via the url or location history seems inadvisable.
I think you'd save yourself a lot of pain by using context or setting up a simple orthogonal store to keep track of it as the user navigates.
Here's a sketch of how you might do it with context. Assuming the provider is above the router in the component hierarchy, the form state will persist through navigation (though not through page reloads). (I haven't tested any of this code. This is just to give you a sense of it.)
const [formState, setFormState] = useState({});
<FormStateContext.Provider value={formState}>
<Form onChange={setFormState} />
</FormStateContext.Provider>
const Form = ({ onChange }) => {
const formState = useContext(FormStateContext);
return (
<input name="username"
value={formState.username}
onChange={(e) => setFormState({ ...formState, username: e.target.value })}
/>
);
}
const Confirmation = () => {
const formState = useContext(FormStateContext);
return (
<div>Your Name: {formState.username}</div>
);
}
If your components aren't that big, you could do something like this instead of using a different route :
import React from "react";
import { render } from "react-dom";
import "./style.css";
const App = () => {
const [state, setState] = React.useState({isConfirmationMode: false});
const handleChange = e => setState({...state, [e.target.name]: e.target.value});
const confirm = () => {
console.log('confirmed');
// Here send you data or whatever you want
// then redirect wherever you want, I just display the form again
setState({...state, isConfirmationMode: false});
}
const cancel = () => {
// juste display the form again
setState({...state, isConfirmationMode: false});
}
const displayForm = () => (
<div>
name : <input type="text" name="name" value={state.name} onChange={handleChange} />
<button onClick={() => setState({...state, isConfirmationMode: true})}>Send</button>
</div>
);
return state.isConfirmationMode ?
<Confirmation name={state.name} confirm={confirm} cancel={cancel} /> :
displayForm()
};
// Here I created 'confirm' and 'cancel', but you might only need 'cancel'
const Confirmation = ({name, confirm, cancel}) => {
return (
<div>
Are you {name} ?<br />
<button onClick={confirm}>Confirm</button>
<button onClick={cancel}>Cancel</button>
</div>
);
}
render(<App />, document.getElementById("root"));
Here is the repro on Stackblitz. The idea is just to either display the form or a confirmation depending on the state of a simple boolean (I separated the confirmation in another component but here it could be part of the first one).

Redirecting onClick

This is a weird situation. So I have this Login component:
export default class Login extends React.Component {
componentDidMount() {
var email = document.getElementById('email').value;
var password = document.getElementById('password').value;
firebase
.auth()
.signInWithEmailAndPassword(email, password)
.then(() => document.getElementById('close').click())
.catch(e => console.log(e));
}
render() {
if (firebase.auth().currentUser === null)
return '';
else return <Redirect to='/questions'/>
}
}
And this is my LoginForm
export default class LoginFormComponent extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<Modal
show={this.props.show}
onHide={() => this.props.changeShowState(false)}>
<Modal.Header
closeButton>
<Modal.Title> Zaloguj sie </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={<Login/>}>Zaloguj</Button>
{/*The problem is ^^^^^ here*/}
</form>
</Modal.Body>
<Modal.Footer>
<Button id="close" onClick={() => this.props.changeShowState(false)}>Close</Button>
</Modal.Footer>
</Modal>
)
}
}
The whole problem is, I want to redirect the page, after the user logs in. I did some research and I figured I have to do it this way.
The problem is the <Login/> element is not rendered. I'm guessing it's just not how React works, but I have no other idea how to get it done. All I want to do is redirect a user after loging in.
You don't have to have it in two components. Your main problem is that you cannot give a React component as an onClick handler.
My take on it would be:
export default class LoginFormComponent extends React.Component {
state = { loggedIn: false, error: false }
constructor(props) {
super(props);
}
handleSubmit() {
var email = document.getElementById('email').value;
var password = document.getElementById('password').value;
firebase
.auth()
.signInWithEmailAndPassword(email, password)
.then(() => document.getElementById('close').click())
.catch(e => console.log(e));
if(firebase.auth().currentUser === null)
this.setState({ loggedIn: false, error: true });
else
this.setState({ loggedIn: true, error: false});
}
render() {
return (
<form onSubmit={this.handleSubmit}>
{ this.state.error && <span>Could not log in.</span> }
{ this.state.loggedIn && <Redirect to='/questions' /> }
<FormControl
id="email"
type="email"
label="Email address"
placeholder="Enter email"/>
<FormControl id="password" label="Password" type="password"/>
<Button type="submit">Zaloguj</Button>
</form>
)
}
}
I think that you don't have understand how React works.
First of all you don't have to use "document.getElementById('email').value", because React works using state and props.
After that you cannot pass a component to the onClick event handler. The onClick wants a function.
If you want to redirect user you can create a method like:
loginUser(){
window.location = "https:..";
}
Your button will be:
<Button onClick={this.loginUser.bind(this)}>Zaloguj</Button>
However when you work with React is really difficult that you need redirects. Seeing your components it seems like you want to handle React in the PHP way (Login component cannot read data with getElementById in the ComponentDidMount method. You have to use react-router for the app routing and you can use something like MobX or Redux to store user login data. You can even pass props and not use MobX or Redux).

ReactJS is no Rendering Component

I took this all from this example. Creating a React Login Page
So I cannot take any credit. However, this page works fine. I am trying to retro fit it to pushing a React Video Player page.
My code is as follows (Snippet from Axios Post):
if (response.status == 200) {
console.log("Login successfull");
var videoPlayer = [];
this.setState( {isLoggedIn : true });
videoPlayer.push(<Videoplayer appContext={self.props.appContext} parentContext={this} />);
self.props.appContext.setState({loginPage: [], videoPlayer: videoPlayer});
The existing code was this:
var uploadScreen=[];
uploadScreen.push(<UploadScreen appContext={self.props.appContext}/>);
self.props.appContext.setState({loginPage: [], uploadScreen: uploadScreen})
render() {
var browserHistory = createBrowserHistory();
if(this.state.isAuthenticated) {
return <Redirect to={'/VideoPlayer '}/>
}
return(
//<Router history={ browserHistory }>
//<Route path="/VideoPlayer" component={() => <VideoPlayer title="Arlo Video" style="home-header"/> }/>
<div>
<MuiThemeProvider>
<div>
<AppBar
title="Login"
/>
<TextField
hintText="Enter your Username"
id = "username"
floatingLabelText="Username"
onChange={(event, newValue) => this.setState({username: newValue})}
/>
<br/>
<TextField
type="password"
id = "password"
hintText="Enter your Password"
floatingLabelText="Password"
onChange={(event, newValue) => this.setState({password: newValue})}
/>
<br/>
<RaisedButton label="Submit" primary={true} style={style}
onClick={(event) => this.handleClick(event)}/>
</div>
</MuiThemeProvider>
</div>
// </Router>
)
}
}
My code does not render the VideoPlayer Page and that code is from the standard example at this github
ReactJS Video Player
I have a feeling that it has to do with Context but I don't know enough about React or how to debug this using the Chrome tools. My backend is Django. I'm almost thinking of going back to using Jquery so I can get pages at least functioning but wanted to try and learn React.
Any help would be great. The code above is just testing code so I could get some thing functional.
Updating the state by returning null, empty array [] or any initial value will not re-render the component. You need to change the state value to something else and then only the component will be re-rendered.
You may also take care of setState will not re-render the component when shouldComponentUpdate hook returns false.
The offending code was the following:
AppContext={self.props.appContext}
Which was a typo to this:
videoPlayer.push(<Videoplayer appContext={self.props.appContext} parentContext={this} />)

How to submit a form in React Native

I feel crazy asking this question here, but I can't find any good tutorials for how to submit a form and capture the data for RN. Everything I do find is someone pushing a library "just npm install react-native-form-genie-magic-box and call it in your project"...
but I just want to know - How to submit a form in vanilla React Native.
Sample code:
AuthContainer
class AuthContainer extends Component {
render() {
const { errorMessage, handleLogin } = this.props
return (
<Login
errorMessage={errorMessage}
onLoginClick={(e) => handleLogin(e)}
/>
)
}
}
.....
const mapDispatchToProps = (dispatch) => {
return {
handleLogin: (e) => {
e.preventDefault()
const form = e.target
const data = serialize(form, {hash: true})
const creds = { email:data.email, password: data.password }
dispatch(loginUser(creds))
},
}
}
Login
import { Container, Content, Form, Item, Input, Label, Button, Text } from 'native-base';
....
const Login = ({errorMessage, onLoginClick}) => {
return (
<Container>
<Content>
<Form >
{errorMessage &&
<Text>{errorMessage}</Text>
}
<Item floatingLabel>
<Label>Email</Label>
<Input
type="email"
name="email"
/>
</Item>
<Item floatingLabel last>
<Label>Password</Label>
<Input secureTextEntry={true} />
</Item>
<Button onPress={onLoginClick} ><Text>Sign in</Text></Button>
</Form>
</Content>
</Container>
)
}
Question: How can I just capture the submitted email and password in AuthContainer's handleLogin function?
On the <input you need to add something like this, example:
<Input onChangeText={(text) => this.setState({username: text})} value={this.state.username}
And when you use the onPress function you just need to get the this.state.username and use it when you want.
I don't usually do a function that handle the Login or something in other .js so you need to pass the this.state.username to the page that handles it.
What i usually do if I really need to pass something to other page is using GLOBALS, example:
// globals.js
module.exports = {
username: '',
};
And then to use the globals.js
// import the globals.js
GLOBAL = require('./globals');
<Input onChangeText={(text) => this_onButtonPressed(text) value={this.state.username}/>
_onButtonPressed(text){
GLOBAL.username = this.state.username
// call function that handles it
}
And then on the page that handles it you need to import it again and just use GLOBAL.username.
If you didn't understand it tell me I will try to explain it better, I need to know if you want to handle the login on a different .js or it can be on the .js that has the Form (its easier like this)
Hi you can use the following url for form implementation in react.
https://github.com/davidkpiano/react-redux-form/issues/383

Categories

Resources