questions about react controlled text field? - reactjs

You can see below sample code, it's a typical controlled field.
export default class NameForm extends Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
render() {
return (
<form>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
I can understand that if I type any text in the field, the value will be reflected into the text field due to the setState function and the value props of the input.
But if I changed the input to below. I removed the value props.
<input type="text" onChange={this.handleChange} />
Currently, If I typed any value in the field, the value will be still there, but as per my knowledge of React, the input will be rendered after the setState, but the props value is not set, the input will be cleared in my opinion, Could you please explain it in detail for me?

I think you miss the important point here. After setState is called the component render will be called - but this will not recreate the input component from scratch. Internally react will check and apply only changes that has happened to the actual DOM.
In your case no differences to the input were made - therefore it will stay as it is.
This process is called reconciliation and you can read more about it here.

Setting value on an input element makes it a controlled input. Controlled inputs always show the value provided in their prop. For example if you have this input:
<input value='constant' onChange={({target: {value}}) => this.setState({value})}/>
User can't change its value. but if you don't provide value prop for your input it's not a controlled value and it shows the value user enters in. This same thing is true for checkboxes, radio buttons, etc.
So in your case it updates based on user input because it's not a controlled component (because you didn't provide value for it.)
If you want to set initial value for a component but keep it uncontrolled (user can change the value of component) you can set the initial value with defaultValue prop.
You can read more here:
https://facebook.github.io/react/docs/forms.html

You shouldn't be setting the value, just the defaultValue, like this
<input type="text" defaultValue={this.state.value}
The handleChange will then make sure the state is updated

Related

Not getting any console.log data from first name field box

import React, { useState } from "react";
import "../styles.css";
const FormBox = () => {
const [value, setValue] = useState({
fName: "",
lName: "",
});
const [textArea, setTextArea] = useState("");
const handleSumbit = (e) => {
e.preventDefault();
console.log(value);
console.log(textArea);
};
return (
<div className="center">
<form onSubmit={handleSumbit}>
<div>
{/*This is how to set a control form box */}
<input
placeholder="First Name"
type="text"
value={value.fName}
onChange={(e) => setValue(e.target.value)}
/>
</div>
<div>
{/*This is how to set a control form box */}
<input
placeholder="Last Name"
type="text"
value={value.lName}
onChange={(e) => setValue(e.target.value)}
/>
</div>
<div>
<textarea
value={textArea}
onChange={(e) => setTextArea(e.target.value)}
></textarea>
<div>
<input type="submit" />
</div>
</div>
</form>
</div>
);
};
export default FormBox;
I'm working on a basic React project and I'm currently getting the message "Warning: A component is changing a controlled input to be uncontrolled. This is likely caused by the value changing from a defined to undefined, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: https://reactjs.org/link/controlled-components
input"
Another problem is that I'm not getting any info when typing in first name field box. I do get back last name and textarea.
Thank you so much and happy holiday
You are having two issues:
Not getting info from the first name field
The uncontrolled vs controlled warning
Issue 1.
This actually happens because on both the first name and last name inputs you are doing setValue(e.target.value). setValue() sets the object that is originally initialized as { fName: '', lName: '' }. This means that even though the value is an object at first, when writing on any of the fields, it will become a simple string, either the first or last name.
This problem does not happen with the text area, since you keep track of its value on a separate state, textArea. That's one way to fix your issue, hold both fName and lName on separate useState calls. Another way is to replace the old value object with a new one that works as expected, for example:
<input
placeholder="First Name"
type="text"
value={value.fName}
onChange={(e) => setValue({ ...value, fName: e.target.value })}
/>
I would also suggest avoiding abbreviations whenever possible. You could've used firstName and lastName instead as variable names. It really goes a long way improving readability.
Issue 2.
This warning is a side effect of the first issue. It appears because you are using controlled inputs but when writing on one of them the object is turned into a string and both value.fName and value.lName are undefined. Controlled inputs are not supposed to have undefined as a value, since that's usually an indicator that you want to use uncontrolled inputs. The empty value should be an empty string, which was intended and will be fixed if you fix the first issue.
There's another stack overflow sharing the differences between controlled and uncontrolled components: What are React controlled components and uncontrolled components?
A Controlled Component is one that takes its current value through props and notifies changes through callbacks like onChange. A parent component "controls" it by handling the callback and managing its own state and passing the new values as props to the controlled component. You could also call this a "dumb component".
A Uncontrolled Component is one that stores its own state internally, and you query the DOM using a ref to find its current value when you need it. This is a bit more like traditional HTML.
The important bit to note here is that an uncontrolled component doesn't receive a value prop and hence its value is undefined.

React Controlled Component

import React, { useState } from "react";
function App() {
const [name, setName] = useState("");
const [headingText, setHeadingText] = useState("");
function handleChange(event) {
// console.log(event.target.value);
setName(event.target.value);
}
function handleClick(event) {
setHeadingText(name);
//after everything is done
event.preventDefault();
}
return (
<div className="container">
<h1>Hello {headingText}</h1>
<form onSubmit={handleClick}>
<input
onChange={handleChange}
type="text"
placeholder="What's your name?"
value={name}
/>
<button type="submit">Submit</button>
</form>
</div>
);
}
export default App;
I will use the above code for asking my question.
input element handles its state change via user input(value attribute) but react uses state variable to handle state change. In order to have one constant value we set react variable to handle state change of form element as well.
value={name}
but if we do so and then access the value entered by the user through event.target.value, How is it able to track what user enters in the input field as we have set value={name}. Shouldn't event.target.value give us the value of name variable instead of user input
I think like this.
value={name} is external state for input element. In React term it's props.
And event.target.value comes from the internal state of input element right after the user keypress happens. In react terms it's a state.
So you change your internal state from user input. It's event.target.value
And you set input element's prop when parent component rerenders. it's value={name}
The answer is that react is 1 way data flow. It's not bound both ways like angularjs or Angular [(ngModel)] is. Meaning that internal DOM element state updates do not take effect until the component is re-rendered - and the component will not re-render until the component state is changed. Therefore, the value in the input is not being "set" by react until state changes. You can modify the value in the input as much as you'd like, and react won't do anything until the onChange handler that then updates state is triggered. Only then will it re-render the component and put the new value in the input box.
Until the re-render happens, the react state and the internal state of the <input> element are out of sync. event.target.value reads back the internal state of the input element, whereas {name} reads back the value of the name property on the, in this case, App component. The two get resync'd when the component is re-rendered.

How is input in react being handled?

** I think I just struggle with this long enough to ask here...so what is the difference between onChange and value here in the input element? To me it seems that both are setting the term to the input text**
class SubmitForm extends React.Component {
state = { term: '' };
render() {
return(
<form>
<input
type='text'
className='input'
placeholder='Enter Item'
value={this.state.term}
onChange={(e) => this.setState({term: e.target.value})}
/>
<button className='button'>Submit</button>
</form>
);
}
}
value is the value of the input text box to the letter. when you are trying to update the input text box, onChange event will be emitted and here, you should update the variable that indicates value.
If you don't specify onChange event, input textbox is not updated even though you press any key because input box value is always indicated with the same value - this.state.term.
This is not specific to reactJS, the value attribute on an html input is what the current value of the input is, the onChange attribute does something (in your case updating the state thus updating the value) whenever the a change happens to the input.

Posting changed form field values in React

I have a form which is passed a value through props, and submits to an endpoint to update a users information. However, I'm unable to send an edited value of the text input field, as its state needs to be managed and updated when a user changes its value, but having trouble setting/updating the state of the input when the user changes the value, allowing a different value to be posted.
class DisplayNameModal extends React.Component {
constructor (props){
super(props)
this.state = {
displayName: this.props.displayName,
email: this.props.email.split('#')[0]
}
this.updateDisplayName = this.updateDisplayName.bind(this)
}
updateDisplayName () {
const email = this.props.email
const displayName = this.state.displayName
const user = {
email,
displayName
}
superagent
.put('/api/user')
.send({user})
.then(this.closeModal)
}
handleDisplayNameChange = e => this.setState({ displayName: e.target.value })
render (props) {
const {contentStrings} = this.props.config
return (
<div>
{ !this.props.displayNameModalActive &&
<div className='display-name-container' style={{ backgroundImage: `url(${this.props.bgImgUrl})` }}>
<div className='display-name-content'>
<h2 className='heading'>{contentStrings.displayNameModal.heading}</h2>
<p>{contentStrings.displayNameModal.subHeading}</p>
<input type="text"
defaultValue={this.state.displayName}
onChange={this.handleDisplayNameChange}
minLength="3"
maxLength="15"/>
<button
type='submit'
onClick={this.updateDisplayName}
className='btn btn--primary btn--md'>
<span>{contentStrings.displayNameModal.button}</span>
</button>
<p className='cancel'>{contentStrings.displayNameModal.cancel}</p>
</div>
</div>
}
</div>
)
}
}
export default DisplayNameModal
I think you need an onChange on your <input /> to update displayName on component state.
handleDisplayNameChange = e => this.setState({ displayName: e.target.value });
<input type="text"
value={this.state.displayName}
minLength="3"
maxLength="15"
onChange={this.handleDisplayNameChange}
/>
and instead of defaultValue, use value to make it a controlled input
So then in your updateDisplayName, you would use this.state.displayName instead of this.props.displayName. The prop is just being used to set the initial component state value, allowing it to be edited.
onChange event, call a method, and inside it use this.setState to set the changed text to state as you type in Input box.
On submit, use the updated State value to pass it to the API.
In this way, you can maintain updated value in local state.
You are using uncontrolled input element, ie React doesnt know, wats going on with ur input element.
In order, for React to know about it, it hould be made controlled component.
This can be done by connecting it value to the the state of the component,
Check example below.
This means, that at any time, react will know, wat is the value of the input element
Controlled Components
In HTML, form elements such as , , and typically maintain their own state and update it based on user input. In React, mutable state is typically kept in the state property of components, and only updated with setState().
We can combine the two by making the React state be the “single source of truth”. Then the React component that renders a form also controls what happens in that form on subsequent user input. An input form element whose value is controlled by React in this way is called a “controlled component”.
For example, if we want to make the previous example log the name when it is submitted, we can write the form as a controlled component:
class NameForm extends React.Component {
constructor(props) {
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});
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
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>
);
}
}
Since the value attribute is set on our form element, the displayed value will always be this.state.value, making the React state the source of truth. Since handleChange runs on every keystroke to update the React state, the displayed value will update as the user types.
With a controlled component, every state mutation will have an associated handler function. This makes it straightforward to modify or validate user input. For example, if we wanted to enforce that names are written with all uppercase letters, we could write handleChange as:
handleChange(event) {
this.setState({value: event.target.value.toUpperCase()});
}

In React, why the input can be kept

In React, when I have a input and user types something, I will call setState to save it. We know that when state is changed, the component would be re-rendered.
My question is in this case, why re-render would not reset the input? In my understanding, if we want to keep the input, we should use
<input type="text" value={ this.state.input } onChange={(e) => this.handle(e) }
I've created a jsfiddle
Created another jsfiddle. Now when user input anything, the className would be changed, so definitely dom tree is changed. but still the input is not reset after each re-rendering
What you are seeing is the difference between a controlled and an uncontrolled input. The input HTML element is inherently stateful, with or without React i.e. it will maintain the value of whatever is typed into it. This is why if we render:
<input type='text' />
Then whatever is typed will be seen in the input, with no help from React. This is an uncontrolled component, meaning that the value of the input is not controlled by React.
If we set a value prop on the input:
<input type='text' value='' />
Then the inherent state of the input is overridden by the value prop. In the above example it is an unchanging empty string, so no entered characters will ever appear in the input.
So if we take the code from your first fiddle:
<input type="text" onChange={ (e) => this.handle(e) } />
There is no value prop set, so the value shown within the input comes from the HTML elements inherent statefulness, not from React. When the component re-renders, the new state and props are irrelevant to the value displayed within the input - again, because this value comes from the state of the HTML element, not from React.

Resources