Let’s build a simple counter button in React that will update every time it is clicked.

To start, create a new React app called counter using create-react-app.

$ npx create-react-app counter
$ cd counter
$ npm start

Open your web browser to http://localhost:3000/ to see the standard React welcome page. We only need to update the src/App.js file in this tutorial.

Counter component

Now we need to create our Counter component. First we can add it to App. Then create Counter and include a static button which will be set to 0.

// src/App.js
class App extends Component {
  render() {
    return (
      <div className="App">
        <Counter />
      </div>
    );
  }
}

class Counter extends Component {

  render() {
    return <button>0</button>;
  }
}

export default App;

If you go to http://localhost:3000/ you’ll see our static button now.

Zero clicks

props

Props are a way to pass read-only data from a parent to a child component. They are a good intermediate step before jumping into state, which is how we will eventually make our counter dynamic.

We can add a count prop to our counter and set it to a value of 5. Then we can reference this prop as {this.props.count} in our button. Props are automatically passed down from parent to child component upon rendering.

// src/App.js
import React, { Component } from 'react';
import './App.css';

class App extends Component {
  render() {
    return (
      <div className="App">
        <Counter count={5}/>
      </div>
    );
  }
}

class Counter extends Component {

  render() {
    return <button>{this.props.count}</button>;
  }
}

export default App;

Go back to http://localhost:3000/ and you’ll see our static button now has the value of 5.

Five clicks

State

Now we can swap out props for state. First remove the count prop from App as it is no longer needed. Then set an initial state within Counter to the value of 42. We can then use {this.state.count} to refer to our state instead of the previous {this.props.count}.

// src/App.js
import React, { Component } from 'react';
import './App.css';

class App extends Component {
  render() {
    return (
      <div className="App">
        <Counter />
      </div>
    );
  }
}

class Counter extends Component {
  state = {
    count: 42
  };
  render() {
    return <button>{this.state.count}</button>;
  }
}

export default App;

Our button now shows the initial state value, which is 42.

Forty-two clicks

Event Handlers

Finally we can add an event handler so that whenever the button is clicked, the count is increased by 1. The first step is to use React’s onClick synthetic event. We’ll have it just display an alert whenever the button is clicked.

// src/App.js
import React, { Component } from 'react';
import './App.css';

class App extends Component {
  render() {
    return (
      <div className="App">
        <Counter />
      </div>
    );
  }
}

class Counter extends Component {
  state = {
    count: 0
  };
  render() {
    return <button onClick={alert('Click me...')}>{this.state.count}</button>;
  }
}

export default App;

If you go back to the webpage you’ll see the alert is already present though before we’ve clicked the button…hmmm.

Click me

This happens because anything within a render method is invoked upon page load. The way around this is to use an arrow function so that only the function is loaded and then upon click, it is invoked and displayed our message. Here’s what that looks like.

// src/App.js
...
  render() {
    return <button onClick={() => alert('Button clicked!')}>{this.state.count}</button>;
  }
}
...

Now there is no alert when you visit the webpage but once the button is clicked, our new alert pops up.

Button clicked

Updating State

The final step is to update state whenever the button is clicked. We could inline this function.

// src/App.js
...
  render() {
    return <button onClick={() => this.setState({ counter: this.state.counter + 1})}>{this.state.count}</button>;
  }
}
...

But it is much more readable–and performant–to move this outside of render() to its own method called handleClick which is also an arrow function so that it only fires upon a button click. A nice feature of arrow functions is that this is implicitly set for us so we don’t also have to explicitly bind our function.

// src/App.js
class Counter extends Component {
  state = {
    count: 0
  };
  handleClick = () => {
    this.setState(({ count }) => ({
      count: count + 1
    }));
  };
  render() {
    return <button onClick={this.handleClick}>{this.state.count}</button>;
  }
}

As a final optimization we can use prevState which is built into React. It is a callback function that refers to the previous state and since setState() is asynchronous, using prevState ensures no errors around this timing.

// src/App.js
handleClick = () => {
  this.setState((prevState, { count }) => ({
    count: prevState.count + 1
  }));
};

Here is what our webpage looks like now after 7 clicks. You can do as many as you like.

Seven clicks

Conclusion

This example is pretty simple but it covers some common patterns in React. Components are reusable which means we can stack them one on top of the other in parent-child relationships like App and Counter here. This leads to DRY (Don’t Repeat Yourself) code.

We initially created a static version of our project. Then we added in props, which are read-only. Our event handler was wired up and used an arrow function so that it only worked when selected, not on page load which is the default behavior for any function within render. Then added in an initial state and could make a one word change to our count reference from this.props.count to this.state.count. And finally we created a custom function outside of the render method that applied logic to our event and updated the state via setState().

In a more complex React application we would start using lifecycle methods but this basic example actually covers most of the core React features.




Want to learn more React? I have a list of recommended React books and JavaScript books.