React component rerendering weird - javascript

I'm trying to do a react app where you have a "login" screen (just add a username to go in).
I have used this tutorial to do what I want(basically after submitting a username, it should change the component rendering)
My problem is that each time I click the submit button and the main component re-renders it will change the state of the main component back to default. The result is that it will show the Game component, which is a generic one, for a split second then go back to rendering the login component. What am I doing wrong?
Main component:
class Main extends React.Component {
constructor(props){
super(props);
this.login = this.login.bind(this);
this.state = {
loggedIn : false
};
}
login() {
this.setState({loggedIn: true});
}
render() {
const isLoggedIn = this.state.loggedIn;
return (
<div>
{isLoggedIn
? <Game/>
: <Login login={this.login}/>}
</div>
);
}
}
The login component:
class Login extends React.Component {
constructor(props) {
console.log("HELLO")
super(props);
this.state = {
value: '',
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
event.preventDefault();
}
handleSubmit(event) {
// login/" + this.state.value
fetch("my.api.example")
.then(res => res.json())
.then(
(result) => {
this.onLogin();
},
(error) => {
console.log(error)
}
)
}
onLogin = () => {
this.props.login();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}

Your handleSubmit doesn't have an event.preventDefault() so the form is submitting and refreshing the page.

Related

I want my Input to be visible in the next line once I click on Submit Button :: React

I am unable to print the Input-
Problem- I give an input and click on the button. I want the input to be visible in the next line.
react
import React from 'react';
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
event.preventDefault()
this.setState({value: event.target.value})
}
render() {
return (
<div>
<form onSubmit= {this.handleChange}>
<input type="text"/>
<button>Submit</button>
</form>
<h1>{this.state.value}</h1>
</div>
)
}
}
export default NameForm;
Your passing change handler to submit, this won't work.
You have to keep track of the inputs with state the way I did or you can use Refs to get the values of the inputs.
Your issue could be solved like this
import React from "react";
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = { value: "", show: false };
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
//keep track of input changes
this.setState({ value: event.target.value });
}
handleSubmit(event) {
event.preventDefault();
this.setState({ show: true });
// handle form submission here
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<input onChange={this.handleChange} type="text" />
<button>Submit</button>
</form>
{this.state.show ? (
<div>
<h1>{this.state.value}</h1>
</div>
) : null}
</div>
);
}
}
export default NameForm;

Submit button takes 2 clicks to generate output

I am trying to update state in react only after form is submitted. I have one html form which has 1 text input and a submit button, but it takes 2 click of submit button to actually change the state in react. I am using 2 methods handleSubmit and handleChange.
handleChange look for changes in input field and update the state accordingly.
handleSubmit append the state updated by handleChange to array on form submission
and state contains { itemslist: [], currentitem: "" }
when 1st time submit button is clicked it gives previous value of item (or gives empty array) and at 2nd time it gives array with value present in input field.
below is my full code
import React from 'react';
class App extends React.Component{
constructor(){
super()
this.state = {
currentitem: '',
itemslist: []
}
this.handleChange = this.handleChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
}
handleSubmit(event){
event.preventDefault();
this.setState((prevState) => {
return{
itemslist: prevState.itemslist.concat([this.state.currentitem])
}
})
console.log(this.state.items)
}
handleChange(event){
const {name, value} = event.target
this.setState({ [name] : value })
console.log(this.state.currentitem)
}
render(){
return(
<div>
<form onSubmit={this.handleSubmit} >
<input type='text' placeholder='enter text' name='currentitem' onChange={this.handleChange} value={this.state.currentitem} />
<button type='submit'>Submit</button>
</form>
</div>
)
}
}
export default App;
This answer could be a bit different of your code but this way it will work. Set the button type to button and make the button handle the submit, not the form. Then change the handleSubmit function to what I've got. I've tried it and it does works!:
import React from 'react';
class App extends React.Component{
constructor(){
super()
this.state = {
currentitem: '',
itemslist: []
}
this.handleChange = this.handleChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
}
handleSubmit(e){
e.preventDefault();
const { currentitem, itemslist } = this.state;
const newArray = [
...itemslist,
currentitem
];
this.setState({ itemslist, newArray });
}
handleChange(event){
const {name, value} = event.target
this.setState({ [name] : value })
console.log(this.state.currentitem)
}
render(){
return(
<div>
<form>
<input type='text' placeholder='enter text' name='currentitem' onChange={this.handleChange} value={this.state.currentitem} />
<button type='button' onClick={this.handleSubmit}>Submit</button>
</form>
// In cas eyou want to see the values in the array+
{
this.state.itemsList.map((item) => <p>{item}</>)
}
</div>
)
}
}
export default App;
setState function is asynchronous in react, so you cannot get the updated value immediately. But if you need to get the recent updated value from state, you must use callback function of setState.
this.setState({items: newItems}, () => { console.log(); })
I have modified your example like below to fulfil your requirement.
import React from 'react';
class App extends React.Component {
constructor() {
super();
this.state = {
currentitem: '',
items: []
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(event) {
event.preventDefault();
this.setState((prevState) => {
return {
items: prevState.items.concat([this.state.currentitem])
}
}, () => {
console.log(this.state.items)
});
}
handleChange(event) {
const {name, value} = event.target;
this.setState({[name]: value});
console.log(this.state.currentitem);
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<input type='text' placeholder='enter text' name='currentitem' onChange={this.handleChange}
value={this.state.currentitem}/>
<button type='submit'>Submit</button>
</form>
</div>
)
}
}
export default App;

How to render a new state on the click of a button

So I've been learning react but I'm having some trouble with forms.
I'm able to change a state after clicking a button in a form. However I've only been able to find resources that show me how to alert the new state.
What if I want to render the new state into the DOM?
I don't necessarily need the answer but would love to be shown the resources to figure out the answer myself to help me learn.
Here's what I've got:
import React from 'react';
import ReactDOM from 'react-dom';
class MyForm extends React.Component {
constructor(props) {
super(props);
this.state = { username: '',};
}
myChangeHandler = (e) => {
this.setState ({username: e.target.value});
}
mySubmitHandler = () => {
alert(this.state.username);
}
render() {
return (
<form>
<p>Enter your name, and submit:</p>
<input
type='text'
onChange={this.myChangeHandler}
/>
<button onClick={this.mySubmitHandler}>Submit</button>
</form>
);
}
}
ReactDOM.render(<MyForm />, document.getElementById('root'));
Make a state for display as the part of component state.
class MyForm extends React.Component {
constructor(props) {
super(props);
this.state = { userNameForInput: '', userNameForDisplay: ''}; // add 'userNameForDisplay'
}
myChangeHandler = (e) => {
this.setState ({ userNameForInput: e.target.value });
}
mySubmitHandler = () => {
this.setState ({ userNameForDisplay: this.state.userNameForInput }); // update `userNameForDisplay` by 'userNameForInput'
}
render() {
const { userNameForInput, userNameForDisplay } = this.state
return (
<form>
<p>Enter your name, and submit:</p>
<input
type='text'
onChange={this.myChangeHandler}
/>
<button onClick={this.mySubmitHandler}>Submit</button>
{/* render 'userNameForInput', 'userNameForDisplay' to DOM */}
{/* the format to render js variable in jsx is '{ jsVariable }' */}
<div>UserName From Input(update by input change): { userNameForInput }<div>
<div>UserName For Submit(update by submit button): { userNameForDisplay }<div>
</form>
);
}
}

React form event.preventDefault() not working if handler calls setState on sibling component

Below is a simple case with three components: App, ExmpleDataConsumer, and ExampleForm. App contains the other two.
I want the contents of the textarea in ExampleForm to be transmitted to ExampleDataConsumer's state when the form is submitted. Setting ExampleDataConsumer's state in doParse triggers a render which seems to be causing the form to submit, reloading the page, even though the handleSubmit method of ExampleForm calls event.preventDefault().
If I just log data to the console, preventDefault() works and the page does not refresh. What am I missing? Is this the wrong way to pass state between sibling components? Any help would be most appreciated.
class App extends React.Component {
constructor(props) {
super(props);
this.exampleForm = <ExampleForm doSomething = {this.doParse.bind(this)} />;
this.exampleDataConsumer = <ExampleDataConsumer data="Hello, World!"/>;
}
doParse(data) {
console.log('In App: ', data);
this.exampleDataConsumer.setState({data: data});
}
render() {
return (
<div className="App">
{this.exampleForm}
{this.exampleDataConsumer}
</div>
);
}
}
class ExampleDataConsumer extends React.Component {
constructor(props) {
super(props);
this.state = {
data: props.data
};
}
render() {
return ( <div>{this.state.data}</div>)
}
}
class ExampleForm extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 'Some starter text.'
};
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(event) {
this.props.doSomething(this.state.value);
this.setState({value: ''});
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
<textarea rows="20" cols="200" value={this.state.value} readOnly/>
</label>
<input type="submit" value="Parse" />
</form>
);
}
}
In handleSubmit(event), you should first call the preventDefault() method.
handleSubmit(event) {
event.preventDefault();
this.props.doSomething(this.state.value);
this.setState({value: ''});
}
check this update something in your code . i hope this is helpful for you
class App extends React.Component {
constructor(props) {
super(props);
this.state={
data:'Hello, World!'
}
}
doParse(data) {
console.log('In App: ', data);
this.setState({data: data});
}
render() {
return (
<div className="App">
<ExampleForm doSomething = {this.doParse.bind(this)} />
<ExampleDataConsumer data={this.state.data}/>
</div>
);
}
}
class ExampleDataConsumer extends React.Component {
constructor(props) {
super(props);
this.state = {
data: props.data
};
}
componentWillReceiveProps(nextProps) {
this.setState({data:nextProps.data});
}
render() {
return ( <div>{this.state.data}</div>)
}
}
class ExampleForm extends React.Component {
constructor(props) {
super(props);
this.state = {
value: ''
};
this.handleSubmit = this.handleSubmit.bind(this);
this.dataUpdate = this.dataUpdate.bind(this);
}
handleSubmit(event) {
this.props.doSomething(this.state.value);
this.setState({value: ''});
event.preventDefault();
}
dataUpdate(ev){
this.setState({value:ev.target.value });
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
<textarea rows="20" cols="200" placeholder="Some starter text." value={this.state.value} onChange={this.dataUpdate.bind(this)}/>
</label>
<input type="submit" value="Parse" />
</form>
);
}
}

State of React.js component is not updated when browser auto-completes username

I have the following component in React:
class LoginForm extends React.Component {
constructor(props) {
super(props);
this.state = {username: '', password: '', redirectToReferrer: false};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
const value = event.target.value;
const name = event.target.name;
this.setState({
[name]: value
});
}
handleSubmit(event) {
event.preventDefault();
console.log('A name was submitted: ' + this.state.username);
Auth.authenticate(this.state.username, this.state.password, () => {
this.setState({ redirectToReferrer: true })
})
}
render() {
const { from } = this.props.location.state || { from: { pathname: '/' } }
const { redirectToReferrer } = this.state
if (redirectToReferrer) {
return (
<Redirect to={from}/>
)
}
return (
<div>
<p>You must log in to view the page at {from.pathname}</p>
<form id='loginForm'>
<input type="text" name="username" onChange={this.handleChange} />
<input type="password" name="password" onChange={this.handleChange} />
<button onClick={this.handleSubmit}>Log in</button>
</form>
</div>
)
}
}
When I use the browser auto-complete feature (instead of typing 'admin' I type just 'a' and let browser to fill the rest) the component's state is not update and it submits incorrect value. When I type the username/password all by hand it works correctly. How can I fix this? It's pretty common use case...
It looks like that some browsers have a bug.
You can try to workaround it with Autofill polyfill:
A polyfill to fire a change event when the browser auto fills form fields

Categories