4

I have gone through the Learn React.js (without JSX) series by James Nelson. The series introduces a form implemented with React and shows that the input fields are effectively read-only and then proceed to explain how to properly change the values. This is not a question on how to do so. Rather, I would like to understand how React prevents the values from changing.

The below code (also in jsfiddle) is a SSCCE:

<!doctype html>
<html>
  <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.7/react.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.7/react-dom.js"></script>
  </head>
  <body>
    <div>
      <h3>simple HTML form (no ReactJS)</h3>
      <form>
        <input type='text' value placeholder='please enter your name'>
      </form>
    </div>
    <div id='react-app'>
      <script type='text/javascript'>
       const rce = React.createElement.bind(React);
       const rcc = React.createClass.bind(React);

       const ContactForm = rcc({
         propTypes: {
           value: React.PropTypes.object.isRequired,
         },
         render: function() {
           return rce('form', {}
                    , rce('input', {type: 'text'
                                  , placeholder: 'please enter your name'
                                  , value: this.props.value.name
                                  , onInput: function(se) {
                                    console.log(se.target.value);
                                  }}))}
         , componentWillMount       : function () {console.log('componentWillMount');}
         , componentDidMount        : function () {console.log('componentDidMount');}
         , componentWillReceiveProps: function () {console.log('componentWillReceiveProps');}
         , shouldComponentUpdate    : function () {console.log('shouldComponentUpdate');}
         , componentWillUpdate      : function () {console.log('componentWillUpdate');}             
         , componentDidUpdate       : function () {console.log('componentDidUpdate');}             
         , componentWillUnmount     : function () {console.log('componentWillUnmount');}
       });

       const ContactView = rcc({

         propTypes: {
           newContact: React.PropTypes.object.isRequired,
         }
         ,render: function () {
           console.log('render called');
           return rce('div', {}
                    , rce('h3', {}, 'why doesn\'t the input field value change?')
               , rce(ContactForm, {
                 value: this.props.newContact
               }))
         }
       });

       var reactApp = rce(ContactView, {newContact: {name: ''}});
       ReactDOM.render(reactApp, document.getElementById('react-app'));
      </script>
  </body>
</html>

The above code produces two html forms: one created with simple HTML, the other with ReactJS. The elements rendered are identical:

For the simple HTML form:

enter image description here

For the HTML form created with ReactJS:

enter image description here

I am logging messages on the console for every component lifecycle method being called. The only ones that appear to be called are render, componentWillMount and componentDidMount (and only at the beginning). When keystrokes are entered in the input text field, no subsequent life cycle methods are called.

So my question is: given that no additional lifecycle methods are called, how does ReactJS manage to prevent the input field value from changing given that its DOM representation is identical to that of a plain HTML form input field (in which the value displayed in my browser when I type into the input field does indeed change)?

Marcus Junius Brutus
  • 26,087
  • 41
  • 189
  • 331
  • I think this is the same question, and it has a comment suggesting React does not directly use a HTML input. Instead, it uses a wrapper: https://stackoverflow.com/questions/53734224/how-does-react-prevent-an-input-with-a-value-property-from-changing – Antoine Weber Mar 22 '22 at 11:33
  • might be related as well: https://stackoverflow.com/questions/68260072/why-can-we-update-a-value-of-a-number-input-using-mouse-wheel It says React dom elements are different than native dom elements – Antoine Weber Mar 22 '22 at 11:46

1 Answers1

3

React uses the concept of virtual DOM. What it does is track changes "virtually" and them replace the "real" DOM with this abstraction.

When a component is (re)rendered, it maps the virtual DOM into a "real" DOM. Once your input field value is bounded to a value received via props, it does not change (because you are not changing props).

If you want to keep the value in sync with what you are typing, you may bind your value to the component state.

Regarding your second question, I didn't quite follow, but you might want to have a look at component's lifecycle. This gives you control over your component when some change happens (for instance, new props are passed down).

Update

As I don't think I was clear, let me elaborate a bit.

According to the docs:

<input type="text" name="title" value="Untitled" />

This renders an input initialized with the value, Untitled. When the user updates the input, the node's value property will change. However, node.getAttribute('value') will still return the value used at initialization time, Untitled.

[Attribute vs value might me confusing. Here's an explanatory post.]

Unlike HTML, React components must represent the state of the view at any point in time and not only at initialization time. For example, in React:

render: function() {
  return <input type="text" name="title" value="Untitled" />;
}

Since this method describes the view at any point in time, the value of the text input should always be Untitled.

Community
  • 1
  • 1
Rafael Quintanilha
  • 1,413
  • 11
  • 10
  • I think I understand about the virtual DOM. My question is how does ReactJS prevent the input field value from changing given that: (a) console.log(`se.target.value`) reports the keystrokes, meaning the value has changed, at least momentarily, (b) `render` is not called again after each keystroke so ReactJS is not given an opportunity to override the (momentarily changed) `se.target.value` and reset it to the value bound via `props`. – Marcus Junius Brutus Mar 29 '16 at 08:23
  • 1
    You are indeed typing, what does not mean you are *assigning* the value. Also, as you are not changing either ```state``` or ```props```, ```render``` is not called again. You may want to check **Advanced Topics** in this link: https://facebook.github.io/react/docs/forms.html. Also, check this answer to understand the difference between *attribute* and *value*: http://stackoverflow.com/questions/6003819/properties-and-attributes-in-html – Rafael Quintanilha Mar 29 '16 at 13:48
  • 1
    I think I get the `attribute` and `value` distinction but regardless, given that the DOM elements used are identical why do I see changes in my browser in the plain HTML case but not in the ReactJS case? (I've updated the question showing the two different DOM `form` elements created to make the point as clear as I can). If ReactJS is attaching listeners behind the scenes to override the changes doesn't that fly against the claim that ReactJS is a library and *not* a framework? – Marcus Junius Brutus Mar 29 '16 at 16:04
  • 4
    @RafaelQuintanilha I guess OP is interested in knowing how the exact same behaviour can be implemented with vanilla JS. When you try to enter something in the input box, it fires some events (keydown, keypress, input, keyup, change). React must be listening to one or more of these events and prevents the default behaviour, and then again setting the value present in vdom while re-rendering. – mg007 Nov 19 '16 at 06:36
  • 2
    @mg007 yes, you put it more clearly and more succinctly than I could: "how can the same behavior be implemented with vanilla JS" – Marcus Junius Brutus Nov 19 '16 at 07:00
  • even i would like to know if such a thing can be implemented in vanilla js. if not, this is a very compelling reason to start using react – vikrant Dec 06 '17 at 11:50
  • Any update on this? This answer clearly does not answer what OP was asking. – Antoine Weber Mar 22 '22 at 11:27