I've seen examples showing how to implement two-way binding in React but none explain how this feature actually functions internally.
In this codepen example taken from React's website, if you comment out line 11:
handleChange(event) {
// this.setState({value: event.target.value});
}
You'll notice how React is enforcing the 2-way binding by making sure the view doesn't update in a way that is inconsistent with the data model even after the user directly modifies the input box. But how is it doing that?
Given how event.target.value
has the input the user just entered in the scope of handleChange
but becomes empty in the view nonetheless, it implies that at some point the value was reset by React. Also, it's not resetting the value simply to empty but according to the latest data model, this can be tested by making the following changes to the code:
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.counter = 0;
}
handleChange(event) {
if (this.counter < 3) {
this.setState({value: event.target.value});
this.counter++;
}
}
This time, the input box changes the first three times and then afterwards it resets according to what the last model state was.
My guess is the following:
Had the state been changed, then the cached Virtual-DOM representation would've been "dirtied" and this would've triggered a re-rendering of the virtual element. However, the rest of the flow I described above would still apply meaning that no new HTML node would've been created and only the attributes of the existing one would've been updated (assuming the element type, e.g. <input>
, didn't change).
That is my best guess as to what the internal mechanism of this feature might be. Please let me know if I'm wrong and if so, what is the real answer to the question.
Thanks!
This is a neat question and I'll give it my best shot if anyone sees any mistakes in my answer please point them out and I'll gladly apply edit corrections. Digging through the React source here's what I've come up with:
HTML is modified and an event is called in the DOM
React queues the event / synthetic event dispatch inside EventPropagators.js
Events are dequeued in order (this event has no effect on the state)
At the end of the event loop, react uses a restoreTarget
to restore controlled components to represent the state in ReactControlledComponent.js :
Use to restore controlled state after a change event has fired.
We perform this translation at the end of the event loop so that we always receive the correct fiber here
This is where the magic happens. The restoreTarget
is a component's state
, so react triggers a regular render()
at this point to redraw the restoreTarget
which I imagine would go through its standard virtual-DOM vs real-DOM reconciliation algorithm.
As per your example, handleChange()
is executed (with or without a state change) after which restoreTarget
is redrawn and renders a component that accurately represents the state at that moment where count = 3
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With