Learning React Hooks 1 useState
Tags: react, typescript, webdev
Published: 2020-03-26 01:06:17

Finally, I researched react hooks quickly last week to consider introducing react hooks to the current react project since most components are using a class.

First, I checked all the components to understand which react hook I need to use.
1. use `state`
2. use `lifecycle method`
3. use `connect` (for redux)

Before dive into `useState()`, would like to think about the benefit. 

### The benefit to introduce hooks
React Hooks allow us to use `state` and lifecycle methods with a functional component.
- less code
- more readable
- easy to test
- possibly improve performance

Also, allow us to create custom hooks(useWhatever) that helps to manage state with useState
For example, useFetchData that fetches data from the API.

In terms of `redux`, react hooks allow us to extract data from the Redux store state without `connect` `useSelector()` and `useDispatch()` allows us to call an action from a component we want to call.

#### Things that React Hooks do not support
reactjs.org/docs/hooks-faq.html#do-hooks-cover-all-use-cases-for-classes


In this post, write 3 simple react apps with `useState()` from a class component
1. counter app
2. display input (object)
3. display input (array)

code
https://github.com/koji/typescript/tree/master/usestate


![counter](https://dev-to-uploads.s3.amazonaws.com/i/qqx1ujsr6zrumuxxbziw.gif)
### counter app by class component
```ts
import React from 'react'

interface Props {
}

interface State {
  counter: number;
}

export class ClassCounter extends React.Component <Props,State> {
  constructor(props:Props) {
    super(props);
    this.state = {
      counter: 0,
    }
  }

  // +1
  handleIncrement = () => {
    this.setState({
      counter: this.state.counter + 1,
    });
  }

  // -1
  handleDecrement = () => {
    this.setState({
      counter: this.state.counter - 1,
    });
  }

  // reset count
  handleReset = () => {
    this.setState({
      counter: 0
    });
  }

  // +10
  handleIncrementTen = () => {
    this.setState({
      counter: this.state.counter + 10,
    });
  }

  render() {
    return (
      <div>
        <h1>class component</h1>
        <p>Count {this.state.counter}</p>
        <br/>
        <button onClick={this.handleIncrement}>increment</button>
        <button onClick={this.handleDecrement}>decrement</button>
        <button onClick={this.handleReset}>reset</button>
        <br />
        <button onClick={this.handleIncrementTen}>increment10</button>
      </div>
    )
  }
}
```

### counter app by hooks 
```ts
import React, { useState } from 'react';

interface Counter {
  counter: number;
}

const FCCounter = () => {
  const initialValue = () => { 
    return 0 
  };
  
  const [counter, setCount] = useState<Counter>(() => initialValue()); // initial value 0

  // +1
  const handleIncrement = () => {
    setCount((prevCount:number) => prevCount + 1);
  }

  // -1
  const handleDecrement = () => {
    setCount((prevCount:number) => prevCount - 1);
  }

  // reset count
  const handleReset = () => {
    setCount({ counter: initialValue });
  }

  // +10
  const incrementTen = () => {
    setCount((prevCount:number) => prevCount + 10);
  }

  return (
    <div>
      <h1>class component</h1>
      <p>Count {counter.counter}</p>
      <br/>
      <button onClick={handleIncrement}>increment</button>
      <button onClick={handleDecrement}>decrement</button>
      <button onClick={handleReset}>reset</button>
      <br/>
      <button onClick={incrementTen}>increment10</button>
    </div>
  )
}

export {
  FCCounter
}
``` 

index.ts
```ts
export { ClassCounter } from './ClassCounter';
export { FCCounter } from './FCCounter';
```

There are 3 differences between a class component and a functional component with useState

1. functional component is using `useState<Counter>({ counter: initialValue})` instead of `this.state = {counter: 0,}`

2. functional component is using `setCount({ counter: counter.counter + 1 });` instead of `this.setState({counter: this.state.counter + 1,});`

3. functional component is using a function instead of a method. In my understanding, this makes testing easier.


![object](https://dev-to-uploads.s3.amazonaws.com/i/k9p85is5xg5f62ry3xd1.gif)
### display input (object) by class component
```ts
import React, { Component } from 'react'

interface State {
  firstName: string;
  lastName: string;
}

interface Props {}

class ClassObjectDisplay extends Component <Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = {
      firstName: '',
      lastName: ''
    };
  }

  handleFirstName = (e: React.FormEvent<HTMLInputElement>) => {
    this.setState({
      firstName: e.currentTarget.value
    });
  }

  handleLastName = (e: React.FormEvent<HTMLInputElement>) => {
    this.setState({
      lastName: e.currentTarget.value
    });
  }

  render() {
    return (
      <div>
        <h1>class component</h1>
        <form>
          <input type="text" value={this.state.firstName} onChange={this.handleFirstName} placeholder="your first name" />
          <input type="text" value={this.state.lastName} onChange={this.handleLastName} placeholder="your last name" />
        </form>
        <h3>My Name: {this.state.firstName} {this.state.lastName}</h3>
      </div>
    )
  }
}

export {
  ClassObjectDisplay
}
```

### display input (object) by hooks
```ts
import React, {useState} from 'react'

interface person {
  firstName: string;
  lastName: string;
}

const FCObjectDisplay =() => {
  const [name, setName] = useState<person>({ firstName: '', lastName: ''});

  const handleFirstName = (e: React.FormEvent<HTMLInputElement>) => {
    setName((prevName: person) => { ...name, firstName: e.currentTarget.value });
  }
  const handleLastName = (e: React.FormEvent<HTMLInputElement>) => {
    setName((prevName: person) => { ...name, lastName: e.currentTarget.value });
  }
  return (
    <div>
      <h1>functional component with hooks</h1>
      <form>
        <input type="text" value={name.firstName} onChange={handleFirstName} placeholder="your first name" />
        <input type="text" value={name.lastName} onChange={handleLastName} placeholder="your last name"/>
      </form>
      <h3>My Name: {name.firstName} {name.lastName}</h3>
    </div>
  )
}

export {
  FCObjectDisplay
}
```
In this case, useState's data structure is `person` that has firstName and lastName. Their initial values are empty.
`useState<person>({ firstName: '', lastName: ''});`

This case needs to use spread syntax to set value properly. If don't use spread, `setName({})` will only set firstName.
`setName({ ...name, firstName: e.currentTarget.value });`

You can try this `setName({ firstName: e.currentTarget.value });` to check the issue.


The array case is almost the same as above.
![array](https://dev-to-uploads.s3.amazonaws.com/i/pmj62w3eu63rflu6zsb2.gif)
https://github.com/koji/typescript/tree/master/usestate/src/components/Array

[Thoughts]
The above samples are using very simple `State` so easy to name variables for `useState`, but if `State` is kind of complicated, probably creating a couple of variables could be good.
I think converting a class component that only uses `State` is good as a starting point.