154 lines
4.6 KiB
Markdown
154 lines
4.6 KiB
Markdown
---
|
|
tags:
|
|
- javascript
|
|
- react
|
|
- react-classes
|
|
- react-hooks
|
|
---
|
|
|
|
# Comparing class components to hook-based components
|
|
|
|
We will look at the same component written with hooks and classes. Both use this
|
|
child component:
|
|
|
|
```jsx
|
|
const CounterBox = (props) => {
|
|
return <div className="output">{props.counterValue}</div>;
|
|
};
|
|
```
|
|
|
|
## Class component
|
|
|
|
```jsx
|
|
import CounterBox from "./CounterBox";
|
|
|
|
class CounterClass extends React.Component {
|
|
constructor(props) {
|
|
super(props);
|
|
this.state = {
|
|
counterValue: 0,
|
|
};
|
|
}
|
|
|
|
addOne() {
|
|
this.setState({
|
|
counterValue: this.state.counterValue + 1,
|
|
});
|
|
}
|
|
|
|
render() {
|
|
return (
|
|
<React.Fragment>
|
|
<button onClick={() => this.addOne()}>Add One</button>
|
|
<CounterBox counterValue={this.state.counterValue} />
|
|
</React.Fragment>
|
|
);
|
|
}
|
|
}
|
|
```
|
|
|
|
## Hook version
|
|
|
|
```jsx
|
|
import React, { useState } from "react";
|
|
import CounterBox from "./CounterBox";
|
|
|
|
function CounterHook() {
|
|
const [counter, setCounter] = useState(0);
|
|
const handleClick = () => setCounter(counter + 1);
|
|
return (
|
|
<React.Fragment>
|
|
<button onClick={handleClick}>Add One</button>
|
|
<CounterBox counterValue={counter}></CounterBox>
|
|
</React.Fragment>
|
|
);
|
|
}
|
|
```
|
|
|
|
## Main differences
|
|
|
|
- Obviously with hooks we use a function, not a class. We can therefore think of
|
|
them as **simple components** that are able to handle state as well as props.
|
|
- We have written the hook as a declared function but we could just as easily
|
|
write it as a function expression or arrow function.
|
|
- With the class, the state versioning is managed via the following properties
|
|
on the `React.Component` class: `this.state` (start state) and `this.setState`
|
|
(the change).
|
|
- In both cases the function that actually creates the change is handled via an
|
|
arrow function. With the hook we use the customary name `handleClick` .
|
|
|
|
- The same process is managed in the hook via the `useState()` method. We
|
|
effectively set 'before' and 'after' destructured variables on this method:
|
|
|
|
```jsx
|
|
const [counter, setCounter] = useState(0);
|
|
|
|
// Schematically:
|
|
const [val, setVal] = useState(initialVal);
|
|
```
|
|
|
|
## Binding with class component state
|
|
|
|
In the class component example we use an inline arrow function to execute
|
|
`addOne()` however the official way to do it is to bind the event via the
|
|
constructor method to avoid the problems created by `this` scope in classes.
|
|
Using arrows is mostly fine and is less verbose, but it is important to
|
|
understand the 'official' approach.
|
|
|
|
The way to write the state change in this way is:
|
|
|
|
```jsx
|
|
class CounterClass extends React.Component{
|
|
constructor(props){
|
|
super(props);
|
|
this.state = { counterValue: 0};
|
|
this.handleClick = this.handleClick.bind(this); // bind the click to the class scope
|
|
}
|
|
|
|
handleClick(event){
|
|
this.setState({
|
|
counterValue: this.state.counterValue + 1;
|
|
});
|
|
}
|
|
|
|
render(){
|
|
return (
|
|
<button type="button"
|
|
onClick={this.handleClick}> // reference the class scope with `this`
|
|
Click Me
|
|
</button>
|
|
<CounterBox counterValue={this.state.counterValue}></CounterBox>
|
|
);
|
|
}
|
|
}
|
|
```
|
|
|
|
## How hooks improve on class components
|
|
|
|
- State, when managed via hooks can be easily decoupled from a specific
|
|
component and reused elsewhere. This also means it can be tested separately
|
|
from any specific component where it is applied. This is much harder to do
|
|
with classes which are closely entangled with the non-state aspects of a
|
|
component through `this` and binding:
|
|
|
|
> With Hooks, you can extract stateful logic from a component so it can be
|
|
> tested independently and reused. Hooks allow you to reuse stateful logic
|
|
> without changing your component hierarchy. This makes it easy to share Hooks
|
|
> among many components or with the community.
|
|
|
|
- Hooks simplify lifestyle methods and the use of `componentDidMount` etc
|
|
through the `useEffect` hook
|
|
- Classes and `this` are confusing generally. Functions are easier to grasp and
|
|
read.
|
|
|
|
## Relation to function components
|
|
|
|
You can think of hooks as function components (previously known as stateless
|
|
components) plus state. Previously function components were 'dumb' components.
|
|
That is, they didn't allow for state management and generally used for layout.
|
|
They could receive data as props but not state. This changes with hooks: you can
|
|
add state to functions and no longer need to use classes exclusively for state.
|
|
In fact you no longer need to use classes at all, unless you want to.
|
|
|
|
> The stateful portion of a function component must be placed before the
|
|
> `return()` statement in the function.
|