Creating a Counter App with useState

In this lesson, we create a tiny counter app with useState in addition to createContext and useContext.

Before you begin

If you haven’t learned React basics and React Hooks basics, please visit the official site before continuing. This course assumes a basic understanding of React and React Hooks.

What we create

We are creating a so-called counter app. There are two numbers that we can increment or decrement. We don’t structure code and put everything into one file in this lesson.

The code

import React, { createContext, useContext, useState } from 'react';

This imports React and some hooks from the library.

const initialState = {
  count1: 0,
  count2: 0,
};

This defines initialState which includes two numbers, count1 and count2

const useValue = () => useState(initialState);

This is a handy custom hook that we can use in a provider below.

const Context = createContext(null);

This creates a context. Its value is initially null, but will be replaced by useValue().

const useGlobalState = () => {
  const value = useContext(Context);
  if (value === null) throw new Error('Please add GlobalStateProvider');
  return value;
};

This is a custom hook to return the context value. It checks whether a provider is used as expected. If the value is null, it is not updated because useValue() never returns null.

const GlobalStateProvider = ({ children }) => (
  <Context.Provider value={useValue()}>{children}</Context.Provider>
);

This is a context provider. This is a component that should be put near the root.

const Counter = ({ name }) => {
  const [state, setState] = useGlobalState();
  const count = state[name] || 0;
  const increment = () => {
    setState({ ...state, [name]: count + 1 });
  };
  const decrement = () => {
    setState({ ...state, [name]: count - 1 });
  };
  return (
    <div>
      {count}
      <button onClick={increment}>+1</button>
      <button onClick={decrement}>-1</button>
    </div>
  );
};

This is a component to display a number. It receives a name prop to specify which number to display, count1 or count2. It has two buttons to increment and decrement the number.

const App = () => (
  <GlobalStateProvider>
    <h1>Count1</h1>
    <Counter name="count1" />
    <Counter name="count1" />
    <h1>Count2</h1>
    <Counter name="count2" />
    <Counter name="count2" />
  </GlobalStateProvider>
);

This is an app component. It has GlobalStateProvider and several Counter components.

export default App;

Finally, we export the app component.

The working example

Check out the app below.

import React from 'react';
require('./style.css');

import ReactDOM from 'react-dom';
import App from './app.js';

ReactDOM.render(
  <App />, 
  document.getElementById('root')
);

Exercise

Try adding a new counter, count3. Interestingly, you don’t need to modify initialState if the initial value of count3 is 0.

Next

In the next lesson, we will learn a different pattern with useReducer.