Cool kids handle state with Hooks
Tags: javascript, react, webdev
Published: 2019-07-02 19:42:51

A React application is basically a set of React components put together to serve the application’s purpose. These components can be either functional or classes. **Functional** components are functions that receive props (properties) and return JSX code that is rendered to the screen. They are categorized as stateless components because they do not utilize state and lifecycle methods.

However, prior to 16.8, if you wanted to have a state in your component or wanted to use life-cycle methods, you would need to make your component as a **class-based** one. Using both types of components has its own advantages when creating an application. However, conversion between them is really annoying and knowing which lifecycle to use, when and how to use it correctly is really challenging when it comes to complex applications.

React 16.8 introduces a new feature: **hooks**. React hooks are a fundamental change since they make it finally possible to create stateful (with state) function components!

This writeup aims to showcase the current state of state management in React. We will take the example of a simple calculator app and implement it using class components logic, then using two different React Hooks: `useState` and `useReducer`. By doing so, we will go through state manipulation in both class and function components.

---

The final result of our calculator app will look as follow:

![calculator-gif](https://cdn-images-1.medium.com/max/1600/1*EWxHgl3Ek64DGyHfCPPYWw.gif)

The calculator accepts two input numbers to performs arithmetic operations according to the selected operator.


```javascript
class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
     ....
    };
  }
  ...
  };
  render() {
    return (
      <form>
        <label>
          <div>
            Number 1 : <input type="text" onChange={this.firstNumUpdate} />
          </div>
          <br />
          <div>
            Number 2 : <input type="text" onChange={this.secondNumUpdate} />
          </div>
          <br />
          <div>
            <select onChange={this.operatorUpdate}>
              <option value="+">+</option>
              <option value="-">-</option>
              <option value="*">*</option>
              <option value="/">/</option>
            </select>
            <br />
            <br />
          </div>
          <input type="button" onClick={this.executeComputation} value="Execute" />
          <div />
          <br />
          <input type="text" value={this.state.result} />
        </label>
      </form>
    );
  }
}
```

---

#I- Using a class component
To remind you again, we resorted to the **class-based** type component to create our App in order to catch the user inputs and update the state values accordingly. The state of our app consisted of the following:

* **firstnumber**: the Number 1 user input,
* **secondnumber**: the Number 2 user input,
* **operator**: the operator the user chooses,
* **result**: the final result of computing Number 1 and Number 2 with the operator.

```javascript
class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      firstnumber: 0,
      secondnumber: 0,
      operator: "+",
      result: 0
    };
    this.firstNumUpdate = this.firstNumUpdate.bind(this);
    this.secondNumUpdate = this.secondNumUpdate.bind(this);
    this.operatorUpdate = this.operatorUpdate.bind(this);
  }
  ...
}
```
We also had our `onChange` and `onClick` handlers that call `this.setState` to update the dynamic stored values in **this.state**:

* `firstNumUpdate`: function that updates the state value firstnumber according to the Number 1 user input,
* `secondNumUpdate`: function that updates the state value secondnumber according to the Number 2 user input,
* `operatorUpdate`: function that updates the state value operator according to the operator user selection.
* `executeComputation`: function that computes the result depending on the Number 1, Number 2 and the chosen operator.

```javascript
firstNumUpdate(evt) {
    this.setState({ firstnumber: Number(evt.target.value) });
  }
  secondNumUpdate(evt) {
    this.setState({ secondnumber: Number(evt.target.value) });
  }
  operatorUpdate(evt) {
    this.setState({ operator: evt.target.value });
  }

  executeComputation = () => {
    let z = null;
    let operator = this.state.operator;
    let firstnumber = this.state.firstnumber;
    let secondnumber = this.state.secondnumber;

    switch (operator) {
      case "+":
        z = firstnumber + secondnumber;
        break;
      case "-":
        z = firstnumber - secondnumber;
        break;
      case "/":
        z = firstnumber / secondnumber;
        break;
      case "*":
        z = firstnumber * secondnumber;
        break;
      default:
        throw new Error();
    }

    this.setState({ ...this.state, result: z });
  };
```
All in all, our class component's return method looks like this:

```javascript
class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      firstnumber: 0,
      secondnumber: 0,
      operator: "+",
      result: 0
    };
    this.firstNumUpdate = this.firstNumUpdate.bind(this);
    this.secondNumUpdate = this.secondNumUpdate.bind(this);
    this.operatorUpdate = this.operatorUpdate.bind(this);
  }
  
  firstNumUpdate(evt) {
    this.setState({ firstnumber: Number(evt.target.value) });
  }
  secondNumUpdate(evt) {
    this.setState({ secondnumber: Number(evt.target.value) });
  }
  operatorUpdate(evt) {
    this.setState({ operator: evt.target.value });
  }

  executeComputation = () => {
    let z = null;
    let operator = this.state.operator;
    let firstnumber = this.state.firstnumber;
    let secondnumber = this.state.secondnumber;

    switch (operator) {
      case "+":
        z = firstnumber + secondnumber;
        break;
      case "-":
        z = firstnumber - secondnumber;
        break;
      case "/":
        z = firstnumber / secondnumber;
        break;
      case "*":
        z = firstnumber * secondnumber;
        break;
      default:
        throw new Error();
    }

    this.setState({ ...this.state, result: z });
  };

  render() {
    return (
      <form>
        <label>
          <div>
            Number 1 : <input type="text" onChange={this.firstNumUpdate} />
          </div>
          <br />
          <div>
            Number 2 : <input type="text" onChange={this.secondNumUpdate} />
          </div>
          <br />
          <div>
            <select onChange={this.operatorUpdate}>
              <option value="+">+</option>
              <option value="-">-</option>
              <option value="*">*</option>
              <option value="/">/</option>
            </select>
            <br />
            <br />
          </div>
          <input
            type="button"
            onClick={this.executeComputation}
            value="Execute"
          />
          <div />
          <br />
          <input type="text" value={this.state.result} />
        </label>
      </form>
    );
  }
}
```
That’s it for our class component! You can check out the code [here](https://codesandbox.io/s/classcalculator-f581y).

Now that we’ve seen how our calculator looks like as a class component, let’s implement it using hooks.

---
#II- Using a functional component
##a) Using the useState hook
Let’s now implement the same application using a functional component and the `useState` hook. We can’t use the **this.state** or **this.setState** properties anymore because we wont use a class-based component. However, our **functional** component with the help of hooks will store and update the state. As mentioned before, hooks are React helper functions to create & manipulate the state of your component.
                   
First let’s import `useState` from React.
 
```js 
import React, { useState } from "react";
```
We then write our App class-based component as a functional component using the following syntax:

```js 
function App() {
```
We then call the `useState` hook function that takes an initial state for the user input and returns an array of two elements:

```js 
const initialState = {
  firstnumber: 0,
  secondnumber: 0,
  operator: "+",
  result: 0
};

function App() {
  const [state, setState] = useState(initialState);
  ...
}
```

* The first element of the array is the **state of the object**,
* The second element is a function that is used to **update that state**.
For the **initial state** we can pass anything, an empty string, 0, null, an empty array, an empty object, whatever kind of state you want to manage initially.

```js 
const [state, setState] = useState(initialState);
```
In our case we decide to put the initial value as “0” for the first number, second number and result input elements, while the operator select input element takes an initial value of “+”.

> _We can of course use a single `useState` call and have all our state (including the numbers, the result, and the operator) in a single object. However, the advisable way is to use multiple calls so you can separate your states, change them independently and focus on **one state at a time**._

When an event is triggered (e.g. the onChange event of the first number input), we use its corresponding state updater function to perform a state update.

```js 
const operatorUpdate = evt => {
  setState({ ...state, operator: evt.target.value });
};

const firstNumUpdate = evt => {
  setState({ ...state, firstnumber: Number(evt.target.value) });
};

const secondNumUpdate = evt => {
  setState({ ...state, secondnumber: Number(evt.target.value) });
};

const executeComputation = () => {
  let z = null;
  let operator = state.operator;
  let firstnumber = state.firstnumber;
  let secondnumber = state.secondnumber;

  switch (operator) {
    default:
      z = firstnumber + secondnumber;
      break;
    case "-":
      z = firstnumber - secondnumber;
      break;
    case "/":
      z = firstnumber / secondnumber;
      break;
    case "*":
      z = firstnumber * secondnumber;
      break;
  }
  
  setState({ ...state, result: z });
};
```

Voilà 🎉! Checkout how our calculator App looks like [here]( https://codesandbox.io/s/usestatecalculator-qey2i)

##b) Using the useReducer hook
`useState` is not the only hook we can use to manipulate our component state. We will see now another hook, `useReducer`, which helps achieve the same result with a different syntax. This hook uses a reducer with two arguments: a state and an action and returns a new state of the app. If you’ve ever used Redux state management library, you will find the `useReducer` hook very familiar to Redux’s reducer.

> _`useReducer` takes a function (called a reducer) which has 2 arguments: the **state** and an **action**. Depending on the action passed to the reducer, a new state of the app is returned. We’ll come back to the reducer in a bit._

###Step1: Configuring the useReducer
We first import the `useReducer`:

```javascript
import React, { useReducer } from "react";
```

We then define the hook like so:

```javascript
const [state, dispatch] = useReducer(reducer, initialState);
```

###Step2: Defining the logic for the user input
Let’s look at our problem again: we wish to add, subtract, multiply or divide 2 numbers. To do that, the user first inputs the 2 numbers. So let’s take a look at our Number 1 and Number 2 input fields.

> _Since the complexity of the tree is not that high and the data of the user input doesn’t need to be passed from a component to another embedded one, we could add a `useState` hook, as we did above, just to manipulate the user input. However, for the sake of the example, let’s use `useReducer` for **everything** , that is, for **handling the user input** and the **logic of the calculator**._

We define two actions: `FIRST_NUM_UPDATE` and `SECOND_NUM_UPDATE` in our reducer, representing the actions to be dispatched or “triggered” when the user inputs **Number 1** or **Number 2** respectively:

```js
function reducer(state, action) {
  const firstnumber = Number(action.firstnumber);
  const secondnumber = Number(action.secondnumber);

  switch (action.type) {
    // User Input actions
    case "FIRST_NUM_UPDATE":
      return {
        ...state,
        firstnumber: firstnumber
      };
    case "SECOND_NUM_UPDATE":
      return {
        ...state,
        secondnumber: secondnumber
      };
    case "OPERATOR_UPDATE":
      return {
        ...state,
        operator: action.operator
      };
    // Computing actions
    case "ADD":
      return {
        ...state,
        result: firstnumber + secondnumber
      };
    case "SUBTRACT":
      return { ...state, result: firstnumber - secondnumber };
    case "MULTIPLY":
      return { ...state, result: firstnumber * secondnumber };
    case "DIVIDE":
      return { ...state, result: firstnumber / secondnumber };
    default:
      throw new Error();
  }
}
```

> _The inputted values for **firstnumber** or **secondnumber** are passed through the action parameter. Depending on which number was inputted, `FIRST_NUM_UPDATE` or `SECOND_NUM_UPDATE` are then dispatched to return a **new state** containing the newly inputed values of **firstnumber** and **secondnumber**._

Now that our reducer handles these actions, let’s actually trigger them whenever the user changes the inputs for the first and second numbers.

```js
const firstNumUpdate = evt => {
    dispatch({
      type: "FIRST_NUM_UPDATE",
      firstnumber: evt.target.value
    });
  };

const secondNumUpdate = evt => {
    dispatch({
      type: "SECOND_NUM_UPDATE",
      secondnumber: evt.target.value
    });
  };
```
We know that we want to dispatch them during the `onChange` of Number 1 and Number 2 input fields. So let’s call `firstNumUpdate` and `secondNumUpdate` in the `onChange` handler for each number input field as such:

```js
<div> Number 1 :
  <input type="text" onChange={evt => firstNumUpdate(evt)} value={state.firstnumber} />
</div>
<br /> 
<div> Number 2 :
  <input type="text" onChange={evt => secondNumUpdate(evt)} value={state.secondnumber} />
</div>
```

Now we have successfully used our reducer to update the state to whatever the user inputs in the number input fields! Let’s do the same to our operator select element:

* We define the `OPERATOR_UPDATE` action to return the selected operator in our reducer function

```js
case "OPERATOR_UPDATE":
      return {
        ...state,
        operator: action.operator
      };
```

* We define a helper method `operatorUpdate` to dispatch the `OPERATOR_UPDATE` action:

```js
const operatorUpdate = evt => {
    const operator = evt.target.value;
    dispatch({
      type: "OPERATOR_UPDATE",
      operator: operator
    });
  };
```

* We call `operatorUpdate` from our onChange handle in our operator select element:

```js
<select onChange={evt => operatorUpdate(evt)}>
      <option value="+">+</option>
      <option value="-">-</option>
      <option value="*">*</option>
      <option value="/">/</option>
</select>
```
Cool, now let’s get our hands dirty with the calculator’s logic!

###Step3: Defining the logic for the calculator
Our calculator gives the user the ability to add, subtract, multiply or divide two numbers. Just from stating the problem we already have 4 reducer actions!

* `ADD` action representing the sum of our numbers

```js
case "ADD":
      return {
        ...state,
        result: Number(action.firstnumber) + Number(action.secondnumber)
      };
```


* `SUBTRACT` action representing the subtraction of our numbers:

```js
case "MULTIPLY":
      return { ...state, result: firstnumber * secondnumber };
```

* `DIVIDE` action representing the division of our numbers:

```js
case "DIVIDE":
      return { ...state, result: firstnumber / secondnumber };
```

Ultimately, our reducer function looks like this:

```js
function reducer(state, action) {
  const firstnumber = Number(action.firstnumber);
  const secondnumber = Number(action.secondnumber);

  switch (action.type) {
    // User Input actions
    case "FIRST_NUM_UPDATE":
      return {
        ...state,
        firstnumber: firstnumber
      };
    case "SECOND_NUM_UPDATE":
      return {
        ...state,
        secondnumber: secondnumber
      };
    case "OPERATOR_UPDATE":
      return {
        ...state,
        operator: action.operator
      };
    // Computing actions
    case "ADD":
      return {
        ...state,
        result: firstnumber + secondnumber
      };
    case "SUBTRACT":
      return { ...state, result: firstnumber - secondnumber };
    case "MULTIPLY":
      return { ...state, result: firstnumber * secondnumber };
    case "DIVIDE":
      return { ...state, result: firstnumber / secondnumber };
    default:
      throw new Error();
  }
}
```

We then define our helper method `executeComputation` to dispatch those actions depending on which operator is used:

```js
const executeComputation = () => {
    const operator = state.operator;

    switch (operator) {
      case "+":
        dispatch({
          type: "ADD",
          firstnumber: state.firstnumber,
          secondnumber: state.secondnumber
        });
        break;

      case "-":
        dispatch({
          type: "SUBTRACT",
          firstnumber: state.firstnumber,
          secondnumber: state.secondnumber
        });
        break;

      case "*":
        dispatch({
          type: "MULTIPLY",
          firstnumber: state.firstnumber,
          secondnumber: state.secondnumber
        });
        break;

      case "/":
        dispatch({
          type: "DIVIDE",
          firstnumber: state.firstnumber,
          secondnumber: state.secondnumber
        });
        break;

      default:
        throw new Error();
    }
  };
```

Now we just need to display the result simply using **state.result**:

```js
<input type="text" value={state.result} />
```

And we’re done 🎉! You can check out what we just did [here](https://codesandbox.io/s/usereducercalculator-cdj4x)


---

#Conclusion

According to the [React documentation](https://reactjs.org/docs/hooks-intro.html), if you are already using class components, you don’t have to switch to hooks. However, you no longer have to use classes just to have state in your component. The `useState` and `useReducer` React hooks provide a nice syntax to achieve the create & manipulate state in a function component.

An important notice concerning the rules of using React hooks.

> _Hooks are used at the top level of your component, for example; you can’t call the hook function nested in another function , or in an if statement or in a loop. Moreover, they have to be component functions that take props and returns JSX._

Some other cool ones to look at would be:

* `useContext`: Accepts a `React.createContext` context object and returns the current context value for that context.
* `useEffect`: Similar to `componentDidMount` and `componentDidUpdate`.
* `useCallback`: Returns a memoized callback.

_To read more about Hooks, checkout the [Hooks API](https://reactjs.org/docs/hooks-reference.html)._

Did you like this article? Was it helpful? Do you have any suggestions to improve it? If you have any thoughts or comments, we would love to hear them!

>_**Notice**: My sister [@anssamghezala](https://dev.to/anssamghezala) and I are both learning React and publish articles **every month**. Follow us on twitter [@amir_ghezala](https://twitter.com/amir_ghezala) and [@anssam_ghezala](https://twitter.com/anssam_ghezala) to get in touch! :)_