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.
- use
state
- use
lifecycle method
- 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
- counter app
- display input (object)
- display input (array)
code
https://github.com/koji/typescript/tree/master/usestate
counter app by class component
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
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
export { ClassCounter } from './ClassCounter';
export { FCCounter } from './FCCounter';
There are 3 differences between a class component and a functional component with useState
functional component is using
useState<Counter>({ counter: initialValue})
instead ofthis.state = {counter: 0,}
functional component is using
setCount({ counter: counter.counter + 1 });
instead ofthis.setState({counter: this.state.counter + 1,});
functional component is using a function instead of a method. In my understanding, this makes testing easier.
display input (object) by class component
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
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.
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.