I am looking at a tutorial for binding input in react with state. What I don't understand is why do I need to bind it to the state vs just a local vairable since it won't cause renders.
In my case I have a login form, in the tutorial it is sending message form. The idea is that the value is send to the App.js(parent) on submit using inverse data flow. It looks like this:
class Login extends Component{
constructor(){
super()
this.state = {
username: ''
};
this.login = this.login.bind(this)
this.handleChange = this.handleChange.bind(this)
}
handleChange(e) {
this.setState({
username: e.target.value
});
}
//here I make a post request and then I set the user in app.js
handleSubmit(e) {
e.preventDefault();
fetch('http://localhost:8080/login', {
method: 'post',
body: JSON.stringify(username)
}).then(data => data.json())
.then(data => {
this.props.setUser(data)
this.setState({
username: ''
})
}
}
render(){
return (
<section>
<form onSubmit={this.submit}>
<input placeholder="username"
onChange={this.changeInput} type="text"
value={this.state.username}/>
</form>
</section>
)
}
Is there a reason to use setState vs just a local variable which won't cause a rendering?
You don't have to, you could make it work without ever storing username in the state. All you have to do is listen for a submit event and fetch the input value at that time, using a ref.
class Login extends Component {
handleSubmit(e) {
e.preventDefault();
console.log(this.refs.username.value)
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<input ref="username" type="text" />
<button type="submit">Submit</button>
</form>
);
}
});
However the usual way to do that with React is to store the input value in the state and update the state every time the value change. This is called a controlled component and it ensures that the input value and the state are always consistent with each other.
class Login extends Component {
constructor() {
super()
this.state = {
username: ''
};
}
handleChange(e) {
this.setState({
username: e.target.value
});
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<input onChange={e => this.setState({ username: e.target.value })} type="text" value={this.state.username} />
<button type="submit">Submit</button>
</form>
</div>
)
}
}
Among other things, it makes things easier to validate the input value or modify it if you need. For instance, you could enforce uppercase by converting the state to uppercase whenever the state changes.
The value of the input field has to change to the new value, that is why for the on change event you set the state to the next event value. If you don't set state, the value will be the same even though it is entered by user.
Hope this helps,
Happy Learning.
Related
I am trying to update the number of event from the parent component by using an input form from the child component, but there is something I am not seeing it either doesn't work or shows undefined
class App extends Component {
state = {
numberOfEvents: 32,
};
.....
updateNumberOfEvents = (eventNumber) => {
this.setState({ numberOfEvents: eventNumber });
};
render() {
return (
<div className="App">
<NumberOfEvents updateNumberOfEvents={this.updateNumberOfEvents} />
}
</div>
class NumberOfEvents extends Component {
state = {
numberOfEvents: 32,
};
handleInputChanged = (event) => {
const value = event.target.value;
this.setState({
numberOfEvents: value,
});
this.props.updateNumberOfEvents(value);
};
render() {
const numberOfEvents = this.state.numberOfEvents;
return (
<div className="numberOfEvents">
<form>
<label for="fname"> Number of Events:</label>
<input
type="text"
className="EventsNumber"
value={numberOfEvents}
onChange={this.handleInputChanged}
/>
</form>
</div>
);
}
}
export default NumberOfEvents;
this.setState({
numberOfEvents: value,
}, () => {
this.props.updateNumberOfEvents(value);
}
);
The details are here.
While this answer does make it work and correctly highlights that setState calls are asynchronous, I would suggest removing the local state inside NumberOfEvents entirely, as you currently have multiple sources of truth for your form.
Update your onChange handler:
handleInputChanged = (event) => {
this.props.updateNumberOfEvents(event.target.value);
};
and pass down the value from the parent:
<NumberOfEvents
updateNumberOfEvents={this.updateNumberOfEvents}
numberOfEvents={this.state.numberOfEvents}
/>
and use that value inside your child component:
<input
type="text"
className="EventsNumber"
value={this.props.numberOfEvents}
onChange={this.handleInputChanged}
/>
Having one source of truth is less error prone and easier to maintain, as illustrated by your current bug.
I agree with hotpink but i tried your code on codesandbox...it does not show the value undfined. Here is the link https://codesandbox.io/s/stackoverflow-iow19?file=/src/App.js.
Check console
How can I setState() another input value with a button in the same component in React?
I'm using the onClick event handler on the button.
I want to make the handleClickfunction which I gave it to the button, to target the value of the input
class Search extends Component {
state = {
searchInput: "",
};
handleClick = () => {
this.setState({
searchInput: input.value,
});
};
render() {
return (
<div>
<input type="text"/>
<button onClick={this.handleClick}>Enter</button>
</div>
);
}
}
Your question is not clear, I believe you are asking how to set the value of an input field when you press a button in react.
If that is correct, then you have done most of the work already, all you need to do now is add an <input> tag.
Like this:
<input type="text" value={ this.state.searchInput } />
If I have misunderstood your question then please clarify.
It may be worth reading about how State and Lifecycle work in React Here
Whenever the setState() function is triggered, React automatically runs the render() function in any components where state has changed, rerendering that component with the new state values.
Edit
After clarification I now understand exactly what you want.
You require the use of a ref, like this:
class Search extends Component {
state = {
searchInput: "",
};
handleClick = () => {
this.setState({
searchInput: this.inputText,
});
};
render() {
return (
<div>
<input type="text" ref={(x) => this.inputText = x}/>
<button onClick={this.handleClick}>Enter</button>
</div>
);
}
}
instead of using a button to update the state try this:
<input type="text" onChange={(e) => this.setState({searchInput: e.target.value }) />
I am attempting to use two methods, handleChange and handleSubmit for a login page in react. I have tried to set two values for username and password within state, update the values in state whenever the input are of the form is modified, then submit using the values stored in state. However, my values return undefined when printed to console. (p.s. I am aware I still yet need to encrypt password for all you security gurus).
I'm new to react so there may be an issue with my logic:
Login.js
export default class Login extends React.Component {
constructor(props) {
super(props);
this.state = {uname: '', password: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({uname: event.target.uname, password: event.target.password});
}
handleSubmit(event) {
alert('A username and password was submitted: ' + this.state.uname + this.state.password);
event.preventDefault();
fetch('https://localhost:8080/login', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
uname: this.state.uname,
password: this.state.password,
})
});
}
render() {
return (
<div>
<Header titleName={"Login"}>
<div className="container">
<div className="card"/>
<div className="card">
<h1 className="title">Login</h1>
<form onSubmit={this.handleSubmit}>
<div className="input-container">
<input name="uname" type="text" value={this.state.uname} id="#uname" required="required"
onChange={this.handleChange}/>
<label form="#unamelabel">Username</label>
<div className="bar"/>
</div>
<div className="input-container">
<input name="password" type="password" value={this.state.password} id="#pass" required="required"
onChange={this.handleChange}/>
<label form="#passlabel">Password</label>
<div className="bar"/>
</div>
<div className="button-container">
<button type="submit" value="Submit"><span>Go</span></button>
</div>
<div className="footer">Forgot your password?</div>
</form>
</div>
</div>
</Header>
<Footer/>
</div>
);
}
}
Replace handle change with this
handleInputChange(event) {
const target = event.target;
const value = target.value;
const name = target.name;
this.setState({
[name]: value
});
}
You need to set value of the inputs to the state, not the name of the inputs
I think the handleChange should setState should have event.tagert.name = event.target.value:
handleChange(evt) {
this.setState({
[evt.target.name]: evt.target.value,
});
}
If you want to get the value of an input inside a react statefull component, you should set the ref property of this input like this:
<input name="username" ref={node => this.username = node} />
then calling this.username will return you the actual value of username input.
You don't want to set the state in a submit action since this makes your component rerender each times a letter push your input value.
More in the react doc
The problem lies with handleChange function. In the code posted above, there are two distinct 'event' objects associated with the two form inputs. Each one of them have their own set of properties. For example: 'event.target.name' stores the 'name' attribute specified by 'input' tag while 'event.target.value' stores the data entered by the user. So, change the implementation of handleChange to either
(A) Verbose
handleChange(event) {
if (event.target.name == "uname") {
this.setState({
uname: event.target.value
});
}
if (event.target.name == "password") {
this.setState({
password: event.target.value
});
}
}
where you can match the 'name' attribute to either uname or password and set state accordingly.
(B) Succinct
handleChange(event) {
this.setState({
[event.target.name]: event.target.value
});
}
I have a simple form. If I only have an onSubmit event handler, my inout fields are read only (which I don't want). When I additionally add an onChange event handler to each input field, they stop being read only. My question is:
Do I have to always add an onChange in addition to onSubmit so the input fields are not read only in React
if so, why?
or am I just doing something wrong in my sample code?Thanks
class App extends React.Component {
constructor(props){
super(props);
this.state={
name:''
}
this.testing=this.testing.bind(this);
}
testing(e){
e.preventDefault();
//axios call here, sending data/name
}
render () {
return (
<div>
<form onSubmit={this.testing}>
<input name="name" value={this.state.name}/>
</form>
</div>
)
}
}
Yes, you HAVE TO. You bind inputs value to the this.state.name variable and you have to provide the way for changing this value.
You must read React documentation
https://reactjs.org/docs/forms.html#controlled-components
You set the value for name input, and it always stay this.state.name which is ''. so you have to control it and add a onChange (something like this.setState({ name: event.target.value }))
If you don't want to take control of input just remove the value, and remove name from your state.
I advice you to read about controlled components in react docs
Here is an example of how controlling components will help: (clean up value of input)
class App extends React.Component {
constructor(props){
super(props);
this.state={
name:''
}
this.testing=this.testing.bind(this);
}
testing(e){
e.preventDefault();
//axios call here, sending data/name
}
cleanUp(){
this.setState({ name: "" });
}
render () {
return (
<div>
<form onSubmit={this.testing}>
<input name="name" value={this.state.name} onChange={ (event) => this.setState({name: event.target.value})}/>
<div onClick={this.cleanUp.bind(this)>CleanUp</div>}
</form>
</div>
)
}
}
I was reading about react forms, and I just can read about two ways of managing data in forms.
The first one - refs, putting refs in each data input:
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
this.focus = this.focus.bind(this);
}
focus() {
// Explicitly focus the text input using the raw DOM API
this.textInput.focus();
}
render() {
// Use the `ref` callback to store a reference to the text input DOM
// element in this.textInput.
return (
<div>
<input
type="text"
ref={(input) => { this.textInput = input; }} />
<input
type="button"
value="Focus the text input"
onClick={this.focus}
/>
</div>
);
}
}
and the second one Controlled Forms, putting a handler for each data input:
import React, { Component } from 'react';
import Form from 'react-form-controlled';
export default class Example extends Component {
constructor(props, context) {
super(props, context);
this.state = {
users: [{
firstName: 'Zlatko'
}, {
firstName: 'Livia'
}]
};
}
onChange = (data) => {
this.setState(data);
}
onSubmit = (data) => {
alert(`Hi ${data.users[0].firstName}`);
}
render() {
return (
<Form
value={this.state}
onChange={this.onChange}
onSubmit={this.onSubmit}
>
<fieldset name="users">
<label>
<input name="firstName" />
</label>
</fieldset>
<button type="submit">Submit</button>
</Form>
);
}
}
So, imagine you have a large form with a lot of data input, will you have to declare all the handler functions for each input in the form? (also, the total of bindings in the constructor)
Wouldn't it be convenient to just submit a just read form.input1, form.input2? I mean, somehing like this:
onSubmit(formValues){
payload = [
{'value1': formValues.input1 },
{'value2': formValues.input2 },
...
{'valueN': formValues.inputN },
]
}
without going to reading it from state?
I any case, is it and advantage to have a smart component with a lot of handlers for managing form values? or maybe the other approach, having a refs for each component in the form?
I would suggest to check redux + redux-form combo. Managing form state with these modules is piece of case. You can have functional components without any local state or handlers.