Getting started with React Hooks
React is the mainstream front-end framework. The v16.8 version introduces a new API called React Hooks , which overturns the previous usage.
This API is the future of React and it is necessary to understand it in depth. This article talks about my understanding, briefly introduces its usage, and helps you get started quickly.
A React foundation is required to read this article. If you haven’t learned React yet, you can take a look at its tutorial first .
[Update] I later wrote an article “Easily Learn React Hooks: Take useEffect() as an Example” , which added an explanation of the design philosophy of Hooks. I suggest you read that article first.
1. Disadvantages of component classes
The core of React is components. Before v16.8, the standard notation for components was class. Below is a simple component class.
import React, { Component } from "react"; export default class Button extends Component { constructor() { super(); this.state = { buttonText: "Click me, please" }; this.handleClick = this.handleClick.bind(this); } handleClick() { this.setState(() => { return { buttonText: "Thanks, been clicked!" }; }); } render() { const { buttonText } = this.state; return <button onClick={this.handleClick}>{buttonText}</button>; } }
(Click to view the running result )
This component class is just a button, but as you can see, its code is already very “heavy”. A real React App is composed of multiple classes in layers and layers, and the complexity increases exponentially. Adding Redux again makes it more complicated.
Dan Abramov, the author of Redux, summarized several shortcomings of component classes.
- Large components are difficult to split and refactor, and also difficult to test.
- The business logic is scattered among the various methods of the component, resulting in duplication of logic or associative logic.
- The component class introduces complex programming patterns, such as render props and high-level components.
Two, function components
The React team hopes that components will not become complex containers, but better just pipelines for data flow. Developers can combine pipelines according to their needs. The best way to write components should be functions, not classes.
React has long supported functional components , here is an example.
function Welcome(props) { return <h1>Hello, {props.name}</h1>; }
However, this writing method has major limitations. It must be a pure function, cannot contain state, and does not support life cycle methods, so it cannot replace classes.
The design purpose of React Hooks is to enhance the functional components. You can write a full-featured component without using “classes” at all.
Third, the meaning of Hook
The word Hook means “hook.”
React Hooks means that components should be written as pure functions as much as possible. If external functions and side effects are needed, use hooks to “hook” the external code in. React Hooks are those hooks.
Use any hook for what function you need. React provides some common hooks by default, and you can also encapsulate your own hooks.
All hooks introduce external functions to functions, so React agrees that hooks are always
use
named
with
prefixes for easy identification.
If you want to use the xxx function, the hook is named usexxx.
Here are the four most commonly used hooks provided by React by default.
- useState()
- useContext()
- useReducer ()
- useEffect()
Four, useState (): state hook
useState()
Used to introduce state to functional components.
Pure functions cannot have state, so put the state in the hook.
For the component class earlier in this article, if the user clicks the button, the text of the button will change. The text depends on whether the user clicks or not. This is the state.
Use
useState()
rewrite as follows.
import React, { useState } from "react"; export default function Button() { const [buttonText, setButtonText] = useState("Click me, please"); function handleClick() { return setButtonText("Thanks, been clicked!"); } return <button onClick={handleClick}>{buttonText}</button>; }
(Click to view the running result )
In the above code, the Button component is a function that uses
useState()
hooks to introduce state
internally
.
useState()
This function accepts the initial value of the state as a parameter. The initial value of the above example is the text of the button.
This function returns an array. The first member of the array is a variable (in the example above
buttonText
), which points to the current value of the state.
The second member is a function, used to update the state, the convention is to
set
prefix the variable name of the state (the above example is
setButtonText
).
Five, useContext(): Shared state hook
If you need to share state between components, you can use it
useContext()
.
There are now two components, Navbar and Messages, and we want to share state between them.
<div className="App"> <Navbar/> <Messages/> </div>
The first step is to use the React Context API to establish a Context outside the component.
const AppContext = React.createContext({});
The component packaging code is as follows.
<AppContext.Provider value={{ username: 'superawesome' }}> <div className="App"> <Navbar/> <Messages/> </div> </AppContext.Provider>
In the above code,
AppContext.Provider
a Context object is provided, which can be shared by child components.
The code of the Navbar component is as follows.
const Navbar = () => { const { username } = useContext(AppContext); return ( <div className="navbar"> <p>AwesomeSite</p> <p>{username}</p> </div> ); }
In the above code, the
useContext()
hook function is used to introduce the Context object and obtain
username
properties
from it
.
The code of the Message component is similar.
const Messages = () => { const { username } = useContext(AppContext) return ( <div className="messages"> <h1>Messages</h1> <p>1 message for {username}</p> <p className="message">useContext is awesome!</p> </div> ) }
(Click to view the running result )
Six, useReducer(): action hook
React itself does not provide state management functions, and usually requires the use of external libraries. The most commonly used library in this regard is Redux.
The core concept of Redux is that components issue actions to communicate with state managers.
After the state manager receives the action, it uses the Reducer function to calculate the new state. The form of the Reducer function is
(state, action) => newState
.
useReducers()
Hooks are used to introduce Reducer functions.
const [state, dispatch] = useReducer(reducer, initialState);
The above is
useReducer()
the basic usage, it accepts the Reducer function and the initial value of the state as parameters, and returns an array.
The first member of the array is the current value of the state, and the second member is the
dispatch
function
that sends the action
.
The following is an example of a counter. The Reducer function used to calculate the state is as follows.
const myReducer = (state, action) => { switch(action.type) { case('countUp'): return { ...state, count: state.count + 1 } default: return state; } }
The component code is as follows.
function App() { const [state, dispatch] = useReducer(myReducer, { count: 0 }); return ( <div className="App"> <button onClick={() => dispatch({ type: 'countUp' })}> +1 </button> <p>Count: {state.count}</p> </div> ); }
(Click to view the running result )
Since Hooks can provide shared state and Reducer functions, it can replace Redux in these aspects. However, it cannot provide middleware and time travel. If you need these two functions, you still have to use Redux.
Seven, useEffect(): side effect hook
useEffect()
Used to introduce operations with side effects, the most common is to request data from the server.
componentDidMount
The code
that was placed
inside can now be placed
useEffect()
.
useEffect()
The usage is as follows.
useEffect(() => { // Async Action }, [dependencies])
In the above usage,
useEffect()
two parameters are accepted.
The first parameter is a function, and the asynchronous operation code is placed in it.
The second parameter is an array, which is used to give the dependencies of Effect. As long as this array changes,
useEffect()
it will be executed.
The second parameter can be omitted, and it will be executed every time the component is rendered
useEffect()
.
Let’s look at an example.
const Person = ({ personId }) => { const [loading, setLoading] = useState(true); const [person, setPerson] = useState({}); useEffect(() => { setLoading(true); fetch(`https://swapi.co/api/people/${personId}/`) .then(response => response.json()) .then(data => { setPerson(data); setLoading(false); }); }, [personId]) if (loading === true) { return <p>Loading ...</p> } return <div> <p>You're viewing: {person.name}</p> <p>Height: {person.height}</p> <p>Mass: {person.mass}</p> </div> }
In the above code, whenever the component parameters
personId
change,
useEffect()
it will be executed.
It
useEffect()
will also be executed
when the component is rendered for the first time
.
(Click to view the running result )
8. Create your own Hooks
The Hooks code in the above example can also be encapsulated and turned into a custom Hook for easy sharing.
const usePerson = (personId) => { const [loading, setLoading] = useState(true); const [person, setPerson] = useState({}); useEffect(() => { setLoading(true); fetch(`https://swapi.co/api/people/${personId}/`) .then(response => response.json()) .then(data => { setPerson(data); setLoading(false); }); }, [personId]); return [loading, person]; };
In the above code, it
usePerson()
is a custom Hook.
The Person component uses this new hook to introduce encapsulated logic.
const Person = ({ personId }) => { const [loading, person] = usePerson(personId); if (loading === true) { return <p>Loading ...</p>; } return ( <div> <p>You're viewing: {person.name}</p> <p>Height: {person.height}</p> <p>Mass: {person.mass}</p> </div> ); };
(Click to view the running result )
Nine, reference link
- Can You Replace Redux with React Hooks? , Chris Achard
- Why React Hooks? , Tyler McGinnis
- React Hooks Tutorial for Beginners , Valentino Gagliardi
(over)