Difference between React controlled component and uncontrolled component - reactjs

I am new to react and while learning I come across this example of controlled component.
function App() {
let [fName, setFName]=useState('');
return (
<div className="container">
<h1>Hello {fName }</h1>
<input name ='fname' value={fName} onChange={(e)=> setFName(e.target.value)} type="text" placeholder="What's your first name?" />
</div>
);
}
just adding value={fName} makes is controlled . I don't actually understand what does it mean by controlled component and uncontrolled. Can you explain it from beginner prospective.

An uncontrolled component means that you will let the component itself manage the value. It's own internal mechanism will keep track of it.
Now when you add the value property to the input component, you will start to "control" the component yourself. The value you put into that property, will be the value that will be displayed.
You can literally control the value yourself, by just passing it in as is, or by changing the value before passing it in.

Controlled Components
These components have what is called a callback function that is triggered each time we enter something new in the form element.
Triggering the function will typically store or update what we type in the same React component that displays the form element that was used
Most widespread use it with forms
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>
);
}
}
Unontrolled Components
These components such as <input> typically maintain their own state and update it based on user input.
In other words, they will accept what we type and have the responsibility of remembering it, and in order to retrieve the values they remembered, you have to get it when you need it.
The latter usually happens during form submission. They can be classified under uncontrolled components.
class NameForm extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.input = React.createRef();
}
handleSubmit(event) {
alert('A name was submitted: ' + this.input.current.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" ref={this.input} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}

Here, Reactjs documentation provided explanation.
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/stateless component.
An 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.
React form components support both controlled and uncontrolled usage:
// Uncontrolled:
<input type="text" defaultValue="hey" ref={inputRef} />
// Controlled:
<input type="text" value={this.state.value} onChange={onHandleChange} />

Related

Able to type inside input text field without onChange method

I am learning React and below one is sample code I am trying out. I am able to render this component and able to type in characters in input field without any handleChange() method ? Is this fine ? because what I know is, in order to make input fields available for typing, we need to add handleChange method something like below
handleChange(e) {
this.setState({ [e.target.name]: e.target.value });
}
import React from "react";
class StudentForm extends React.Component {
constructor() {
super();
}
render() {
return (
<div>
<form>
<h1>Student Form</h1>
<input type="text" name="firstname"></input>
</form>
</div>
);
}
}
export default StudentForm;
handleChange is for setting the state value.
without onChange handler you can type in but your value is not getting stored anywhere.
For example, if you try to access your state this.state.firstname you will always get undefined.
You should have controlled component. Which is a simple and cleaner way access and store value in state.
To make your component controlled, you should have value and onChange props on input,
<input type="text" name="firstname" value={this.state.firstname} onChange={this.handleChange.bind(this)}></input>
Yes, consider the following
<input type="text" name="firstname" />
This is an uncrontrolled input which means React doesn't now about it's value nor how to change it. To make an input controlled you need to explicitly specify the value and onChange properties to bind this input to React's state
const Input = () =>{
const [value, setValue] = useState('')
return <input value={value} onChange={e => setValue(e.target.value)} />
}
Now the input is fully controlled by React, which provides the value it must print and a way to change it
After making below changes, I made this input element as controlled element and now I am not able to type in anything without using onChange handler.
import React from "react";
class StudentForm extends React.Component {
constructor() {
super();
this.state = {
firstname: ""
};
}
render() {
return (
<div>
<form>
<h1>Student Form</h1>
<input
type="text"
name="firstname"
value={this.state.firstname}
></input>
</form>
</div>
);
}
}
export default StudentForm;

How access specific DOM in React js

I'm trying to get input tags' HTML inner values when submitting a form.
private handleSubmit = (event: any) => {
event.preventDefault();
console.log(event.currentTarget);
};
When the submit the form, it calls the function handleSubmit and it console logs the following.
Under the form tag, the first div has username value and the second div has password value. I would like to acess the two values. I think I should use DOM to do that, but can't be sure if I'm going for the right direction cuz I found some postings saying using DOM is not recommended.
Can anyone explain how I can acheive this?
Ideally you should update your state as the user enters information, and then access the data from the state. This would also allow you to run any validation on the data prior to it going into the state if you'd like.
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
username: null,
password: null
}
this.submitForm = this.submitForm.bind(this);
this.updateState = this.updateState.bind(this);
}
updateState (e) {
this.setState({[e.target.name]: e.target.value})
}
submitForm (e) {
e.preventDefault();
console.log(this.state);
}
render() {
return (
<div className="App">
<form onSubmit={this.submitForm}>
<input type="text" name="username" placeholder="username" onChange={this.updateState} /><br />
<input type="password" name="password" placeholder="password" onChange={this.updateState} /><br />
<button type="submit">Submit</button>
</form>
</div>
);
}
}
export default App;
The above code does the following:
Stores default values for username and password. While this isn't required, it makes the code more readable
binds this to functions that need to access state
Uses an updateState() function that is called onChange of the inputs
updateState uses the name attribute of the input as the key for the state
You could customize the updateState() function to do some validation, before saving to state if you'd like.
Excessive Rendering
ReactJS is pretty smart to no re-render the REAL DOM if your render() method doesn't actually rely on the state values that were updated; however, if you'd like to prevent ReactJS from even creating the Virtual DOM and comparing, you could utilize the shouldComponentUpdate() lifecycle hook.
In the particular example above, since render doesn't rely on ANYTHING in state, you could simply add the following method:
shouldComponentUpdate(prevState, nextState) {
return false;
}
That will prevent the render method from EVER re-rendering, which is most likely not going to work in a normal component, thus you could do something like this instead, only re-rendering on values you care about.
shouldComponentUpdate(prevState, nextState) {
if (nextState.email !== prevState.email) {
return true
}
return false;
}
Demo
https://repl.it/#AnonymousSB/SO53689072
If you want to use the DOM, once you have the form element (event.currentTarget in your case), you can use the .elements property to access a list of child inputs and buttons.
Alternatively, you can use React refs to keep track of the underlying HTML element when it's rendered.
render() {
return ... <input ref={(e) => this._name = e; } ....> ... ;
}
handleSubmit(e) {
var name = this._name ? this._name.value : '';
....
}
This can achieve what you want to
class Login extends Component{
state={
username:"",
password:""
}
onChange = (event)=>{
event.preventDefault()
this.setState({
[event.target.name]: event.target.value})
}
onSubmit = (event)=>{
event.preventDefault()
// submit whatever is in state from here
console.log(this.state)
}
render(){
return(<div>
<form onSubmit={handleSubmit}>
<input type="text" name="username" onChange={this.onChange} /><br />
<input type="password" name="password" onChange={this.onChange} /><br />
<button type="submit">Submit</button>
</form>
</div>)
}
}

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()});
}

Handling form submissions without handling changes?

I'm new to front-end development so please forgive me if this is an ignorant question. I'm using ReactJS to build a simple SPA with several form components. In the official React docs and other tutorials I've read online, it seems like to be able to handle a form submission, I also have to handle form changes, i.e. update the component state with each keystroke, like so:
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>
);
}
}
This seems wasteful to me since I'm only interested in the final form value that's been submitted. Is there a way to access the value of the submitted form in the submission event handler so that I can only update the state once and eliminate the need to handle any non-submission change?
It may seem wasteful, but that is exactly the React way of doing it:
https://reactjs.org/docs/forms.html
Any way of trying to get around this would be more hacky than efficient. This may help keep you from duplicating code while trying to track all your input values:
When you need to handle multiple controlled input elements, you can add a name attribute to each element and let the handler function choose what to do based on the value of event.target.name.
This way you only need one 'handleChange' method.
A controlled component is not really necessary in this case. Update the input component to:
<input type="text" name="username" required/>
Now handle only the onSubmit. The value can be retrieved at:
event.target.username.value
Add the attribute "required" to the input tag for automatic validation.

Input field in not Editing..How to write onchange function for input field in reactjs

Im sending props value to the input field.When im trying to edit the
field it is not editing..How to write onchange handle function.Can anyone
explain briefly about controlled and uncontrolled inputs
handleUserInput = (e) => {
//what to do here in order to edit the input field
}
render() {
return(
<div>
<div className="info">
<label className="label">Store Name</label>
<input type="text"
ref="storename"
className="form-control"
value={this.props.storeName}
placeholder="Store Name"
onChange={this.handleUserInput}
disabled={this.state.disabled}
/>
</div>
</div>
);
}
Change this:
value={this.props.storeName}
to this (otherwise the value for input is always the same - the value received in props):
value={this.state.storeName}
and copy the props to the state in constructor(you should do that in constructor only if you are using props as an initial value):
constructor(props){
super(props)
this.state = {
storeName: props.storeName
}
}
and
handleUserInput=(e)=>{
this.setState({storeName: e.target.value});
}
Now we have made the input box a controlled element (with an initial value taking from the props though). If you are interested in controlled components more you can look in the docs, it isn't hard concept to grasp.

Resources