React: Proper Explanation for ref vs controlled input and use case for state variable in react ?

Created on 7 May 2017  Â·  5Comments  Â·  Source: facebook/react

I went through the docs of react, about controlled and uncontrolled components. I have created a simple use case where I want to enforce the user to enter only uppercase values in the input field.

  1. In the first case I have used 'ref' with 'onChange' to achieve this and a global object's property to capture the dom node.

Here's the code for that -

https://jsfiddle.net/69z2wepo/78064/

  1. In the second case I have used 'value' property and state with 'onChange'.

Here's the code for that -

https://jsfiddle.net/69z2wepo/78066/

The docs says

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

Well I can validate the user input even when I am not using state, and not syncing the value prop with state as done in the first example above.

  1. So definitely validating user input can be done without using state at all ?
  2. Can you explain why one approach is better than the other ?
  3. What exactly the 'single source of truth' means and why it is so important ?
    In both the cases I am using a global variable of the component which is an object and can be accessed throughout the component.
  1. Also why should I unnecessarily use value = { this.state.input } in example 2, because that would call render on every keystroke, whereas in case 1 render is called only once. So isn't performance in case 1 better than 2 ?

Also from the docs:

To write an uncontrolled component, instead of writing an event handler for every state update, you can use a ref to get form values from the DOM.

Well I need to write an event handler like 'onChange' even when using 'ref' to validate user input at run time as done in case 1. So using event handler with 'ref' is normal ?

Use case for state variable -- From my understanding the only case where I have no other option than to use state variable is when I need to update the view dynamically. Because this.setState() calls render every time it is run .
Here's the code for that -

https://jsfiddle.net/69z2wepo/78068/

I will be grateful if someone could clarify on all the three examples and enhance my understanding of the above concepts.

Most helpful comment

Let's say there is a text field and you typed something into that text field, later when you want to get the value you do something like const input= document.getElementById("id").value;
A Html field stores its value and you can get the element and its value. This is called uncontrolled input. This can be done in react as well using ref. With React, we can render an input field, and tell it what its initial value should be. Let's say you have to prefill an input, but still, need to leave it editable. You will do
something like: <input type="text" value="Hi" />
Now if you put the cursor in that input after the i, and try to type nothing will happen because every time you type, React component will re-render and React will see that the value is supposed to be Hi
and when I type that first char like a and the value becomes Hia, React will overwrite it to Hi again.

So, a Controlled input is when you specify the value. And, in order to update the value, you must also specify an onChange callback for the input. The event in the callback will have the new suggested value from the input. You do something like:

class MyComponent extends Component {  
    constructor(props) {  
        super(props);  

        this.state = {  
            text : "Hi"  
        };  

        this.onChange = this.onChange.bind(this);  
    }  

    onChange(e) {  
        const newText = e.target.value;  
        this.setState({text : newText});  
    }  

    render() {  
        return <input type="text" value={this.state.text} onChange={this.onChange} />  
    }  
}  

Every time you type another character into that textbox, it fires onChange event, where you take the new value and update the state, then re-render the component with the new value. This makes sure you never have trouble with your inputs being out of sync with your data as you always have the values in your state. Controlled components are driven by React itself.
It's the use of <input value={initValue} onChange={OnChangeHandler} /> that makes it controlled component.
Hope this explanation helps a bit to understand why we update state every time there is a change in input.

All 5 comments

Let's say there is a text field and you typed something into that text field, later when you want to get the value you do something like const input= document.getElementById("id").value;
A Html field stores its value and you can get the element and its value. This is called uncontrolled input. This can be done in react as well using ref. With React, we can render an input field, and tell it what its initial value should be. Let's say you have to prefill an input, but still, need to leave it editable. You will do
something like: <input type="text" value="Hi" />
Now if you put the cursor in that input after the i, and try to type nothing will happen because every time you type, React component will re-render and React will see that the value is supposed to be Hi
and when I type that first char like a and the value becomes Hia, React will overwrite it to Hi again.

So, a Controlled input is when you specify the value. And, in order to update the value, you must also specify an onChange callback for the input. The event in the callback will have the new suggested value from the input. You do something like:

class MyComponent extends Component {  
    constructor(props) {  
        super(props);  

        this.state = {  
            text : "Hi"  
        };  

        this.onChange = this.onChange.bind(this);  
    }  

    onChange(e) {  
        const newText = e.target.value;  
        this.setState({text : newText});  
    }  

    render() {  
        return <input type="text" value={this.state.text} onChange={this.onChange} />  
    }  
}  

Every time you type another character into that textbox, it fires onChange event, where you take the new value and update the state, then re-render the component with the new value. This makes sure you never have trouble with your inputs being out of sync with your data as you always have the values in your state. Controlled components are driven by React itself.
It's the use of <input value={initValue} onChange={OnChangeHandler} /> that makes it controlled component.
Hope this explanation helps a bit to understand why we update state every time there is a change in input.

I'd like to open this up based on something I am not finding a bit confusing. (If it would be preferred I open a new issue, I can do that as well. Just figured I post my comment here first to reduce new issue clutter.)

I've been using controlled components for near everything in my application as that has been the recommended way to go, and I subscribe to the idea that React should be in control if this state when possible.

However, I recently went through @ryanflorence's _Advanced React_ course—which is a wonderful collection and breakdown of some powerful (and still uncommon) features and patterns available in React—and the last lecture is on controlled components.

At around 17:40 in the lecture video, Ryan does something very interesting. He writes some code that looks like this:

<Tabs
  defaultActiveIndex={this.state.currentTab}
  onChange={(index) => {
    this.setState({ currentTab: index })
  }}
/>

The context of this component is not necessarily relevant here (although it would help to understand by watching the lecture video, the lecture is free if you create an account); you can mentally replace the above snippet with a native input element.

<input
  defaultValue={this.state.value}
  onChange={(e) => {
    this.setState({ value: e.target.value })
  }}
/>

⚠️ NOTE: if my above statement is incorrect, and form field elements are special cases compared to composite components when talking about controlled vs. uncontrolled components, please correct me.

The intention here is something I haven't seen or heard a lot of noise about. He is _using a change handler with an uncontrolled component_. Ryan then says this (emphasis mine):

_We don't actually need to control [the active index]. Nothing else in the app is changing the active index; […] there are no buttons or anything that changes the currentTab. Even if I need to get the state in a change handler doesn't mean I have to control it. The only time you have to control a component is when the programmer, you, need to change the index, or the value, or whatever the controlled property is. We don't need to control it, we just want to know it here._

— Ryan Florence, _Advanced React_, Lecture 08 on "Controlled Components"

I've watched this video more than handful of times, and implemented a few controllable components myself, but only now have I realized I'm rather confused about how a pattern like this might be seen by the React community at large. Why do the React docs make no mention of this pattern for uncontrolled components?

From what I read in the official docs, uncontrolled components are only ever used in conjunction with refs. However, what if all I want to do is know about the value? Is it an okay pattern to use defaultValue with a change handler for something like an input element if all I'm trying to do is read the value to affect something else in my app?

@indiesquidge, I believe that is still a controlled component.

Note: I'm still green, so hopefully everything I'm saying is correct.

The only difference here is that the function to update the component's state is an inline function. Since there isn't any middle step of altering the input, it's a fairly straight forward inline function.

To my understanding, a component that holds state for the intent to pass this state on submit (e.g. a form), is a controlled component.

I don't think using defaultValue vs value is going to provide much of any difference. The field that is going to be used for, say, posting to info to the server is the component's state. The defaultValue is simply referencing that part of the component's state.

If you don't have data stored in state for this purpose, it's an uncontrolled component. You can use refs instead for an uncontrolled component, however the only functionality I can see losing from using refs is that you can't as easily provide form validation on-the-fly. There's probably other benefits to using a controlled component that I'm not aware of as well.

Please file issues with documentation here: https://github.com/reactjs/reactjs.org

We're no longer tracking them in the main repository, and this discussion is not being read by anyone on the team. Thank you!

@pinakdas163

Now if you put the cursor in that input after the i, and try to type nothing will happen because every time you type, React component will re-render and React will see that the value is supposed to be Hi
and when I type that first char like a and the value becomes Hia, React will overwrite it to Hi again.

I wonder why React re-renders when user types, even if you remove the onChange event hander attribute, in which case, there is no state changes accordingly.

And what's the purpose of the value attribute in the controlled input element? Without the code value={this.state.text}, the React component could still track the text state. Is that the value attribute set for future access like the code document.getElementById("id").value do?

Was this page helpful?
0 / 5 - 0 ratings