Controlled Input Pattern in React

Coming from Angular background, two-way binding is something that is easily accessible and can be used without much thought. Things just seem to magically work when it comes to passing values to and from the view.

React, however, works a little differently. States have to be explicitly set. It’s just how things are done. The entire premise of React is that it will update and render what users see when you, the coder, tells it to. Some people prefer it this way. Some just want to use Angular because they don’t have to write the extra code that comes with it.

Here’s the quick what, why and how two-way binding works in React.

The What and Why

Two-way binding is a pattern that connects a data model to the UI. In React, data binding is a one time event. There is no automated mechanism that updates what the user sees.

This as a result, performs better under load because there is no additional memory space consumed to make room for two-way binding that may or may not be used — and also one of the reasons why some people prefer React over Angular.

React’s fundamental philosophy is to ‘make code predictable’. One way binding takes it back to the bare bones day where expected defaults do not have overrides on them. This means that you only have to write the override once rather than twice if you want to override the override.

In a way, this makes code more predictable because you’ve gone back to its roots rather than a reprocessed version of it.

The How

Two way binding is often required when an event occurs. The two most common events are on change or on click. Change usually occurs inside an input and clicks on buttons. See below on how to implement two way binding in React for these two events.

onChange event

Inside your component’s render() and return, you might have some html code that looks like this:

<input type="text" 
placeholder="this is input one"
onChange={this.inputUpdate} />

The above input’s type is set to text with some placeholder content. In normal HTML, it would have been a change selector. In React, we use a thing called onChange instead. It calls a function called inputUpdate inside {} and uses the reference this to tell JSX that we are referring to a function that’s outside our current scope.

We don’t put () at the end of inputUpdate because it will execute the function right away. However, we just want it execute when onChange occurs and therefore just leave it inside the {} without ().

The above will print an input element on the screen that currently does nothing. To handle change, we need to create state object inside your React component that looks something like this:

state = { inputValueOne: "some text here" }

inputValueOne matches with the id we’ve previously set in the id.

We can now use this.state.inputValueOne to display the text. Back in your HTML, your code will look something like this in order to print the text on the screen.


In essence, what we’re trying to build is a two way relationship and connection that allows us to automatically update what’s printed on the screen and what gets typed into the input field. To do this, we need to call a special React function called setState() , which takes in an object as its parameters to map.

The handler function therefore, should look something like this:

inputUpdate = e => {

When you call a function onClick, it passes in an event object that gives you details about the associated item that the event occurred on. target gives us access to the element and associated selectors. In our case, we want the id to map to our state. You can do this manually by specifying inputValueOne instead of [] but the latter is much more flexible as you only have to write it once. You don’t have to worry if more inputs are latter added and you need the state to update and store your values.

And that’s all the code needed for two-way binding in React for onChange events.

onClick event

onClick two-way event handling is essentially the same as onChange. You need to setup or add to the state object. However, the only main difference is that you need also use e.preventDefault().

This is function is something that is available inside the event object that’s passed in through the function call. preventDefault() stops the page from refreshing, which is the original action that occurs when a button gets clicked and something is expected to happen.

So your code may look something like this:

state = { numberTracker: 1934 };//two way binding for button - onClick event
buttonClickUpdate = e => {
let currentNumber = Math.random();
this.setState({ numberTracker: currentNumber })
};...<button onClick={this.buttonClickUpdate}> Click me!</button>
<p> {this.state.numberTracker} </p>

Inside a loop

Things can get complicated when you need to set and use states inside a loop. Loops is one of those things that you can’t avoid in certain places while building an app. There will always be some sort of list of elements that you need to iterate through to order to remain dynamic.

In React, rather than creating the loops inside the HTML part of the component, I’ve decided to remove it and place it inside a function that iterates through a little block of code that gets pushed to an array and then returned to the view for displaying on the screen.

foodList =["coffee", "potatoes", "drinks", "spices", "bananas", "oranges", "oatmeal", "bread", "flowers"];printMeALoop = () => {
let buttons = [];
let foodList = this.state.foodList;

const addFoodItem = e => {
this.setState({ foodList: foodList });
};, key) {
<button key={key} value={item}
}); return buttons;
render() {
return (

In the code snippet above, we have an array of foodList that we want to display on the screen or users to click on to add to a displayed shopping list (code below).

We do this by creating a function that maps foodList array to what we want to print out onto the screen. We then push this code block into the array we created (buttons = []) which is later returned at the end of the printMeALoop function.

To render the loop, we call it inside our render and return with () because we want to execute it right away.

Now, lets say we want to handle a click event inside the loop. We need to use .bind(this) on addFoodItem in order to successfully call it.

Inside addFoodItem, we don’t update the state directly but on a clone (i.e. let foodList = this.state.foodList;) that we manipulate and push our into. The target.value part is set in our HTML value= part of the loop.

This completes the act of creating a loop that has onClick handlers. In theory, you can do the loop inside the HTML part of your React component and then call the function. However, this gets tricky because your scope is no longer clear and separation of concerns gets split across two different parts of your file. The above pattern allows you to keep everything related together and maintain the boundaries of your scopes.

The code below follows the same patterns and ideas as above, but rather than adding to the state, we’re removing things from it.

removeMeFromLoopPls = e => {
let theGroceries = [];
let foodList = this.state.foodList;

const removeItem = e => {
let index = foodList.indexOf(;
if (index > -1) foodList.splice(index, 1);
this.setState({ foodList: foodList });
};, key) {
<li key={key}>
<button value={item}
}); return theGroceries;
render() {
return (

Final words

For a working code repo, check out my GitHub here. Feel free to fork, clone or download. The React project has all three examples above in working order that you can play with and tweak to see how everything fits together.

Coming from an Angular background where all the above is automated, it took some time getting use to manually setting up states again. However, despite this, doing so has its benefits.

I found myself more aware of what actions I’m coding into my apps. With the state being contained in one space also help prevent leaky patterns. React markets itself as a ‘JavaScript library for building user interfaces’ and it does just that in its own special and strangely efficient way.



About Author /

I code. I write. I hustle. Living the #devLife remotely. Subscribe to my newsletter to stay connected with my latest posts and dev thoughts. Want to collaborate? DM me on LinkedIn

Leave a Comment

Your email address will not be published.

Start typing and press Enter to search