What are hooks?
Hooks are new in Act 16.8. They let you use state and other React functionality without having to write classes
Hooks are functions. There are several types of Hooks, each providing a channel for function Components to use React state and declaration cycles. React provides a number of predefined Hooks for use. Here are some examples:
useState
Let’s look at a piece of code
import React, { useState } from 'react'; Const [count, setCount] = useState(0); const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }Copy the code
You can see that useState has only one entry, which is the initial value of state. The initial value can be a number, string, array, or object, or even a function. When the input parameter is a function, the function is executed only when the component initializes the render:
const [state,setState] = useState(()=>{
const initalState = someExpensiveComputation(props)
return initalState
})
Copy the code
When we need to calculate the current state value from the previous state value, we need to pass in a function. This is similar to setState in class Component, and it is similar to setState in class Component. When the new value passed is the same as the previous value (compared using object.is), no update is triggered.
useEffect
Before explaining useEffect, let’s understand what side effects are. Network requests, subscribing to a module, or DOM operations are examples of side effects that useEffect is specifically designed to deal with. In normal cases, it is not recommended to write side effects code in function Component functions, which is prone to bugs.
In the following class Component example, the side effect code is written in componentDidMount and componentDidUpdate:
class Example extends React.Component { constructor(props) { super(props); this.state = { count: 0 }; } componentDidMount() { document.title = `You clicked ${this.state.count} times`; } componentDidUpdate() { document.title = `You clicked ${this.state.count} times`; } render() { return ( <div> <p>You clicked {this.state.count} times</p> <button onClick={() => this.setState({ count: this.state.count + 1 })}> Click me </button> </div> ); }}Copy the code
UseEffect () {componentDidMount (); useEffect () {useEffect ();
import React, {useState, useEffect} from "react";
function FriendStatus(props) {
const [count,setCount] = useState(0);
useEffect(()=>{
document.title = `You clicked $(count) times`
});
return (
<div>
<p>You clicked {count} times</p>
<button @click={() => setCount(count + 1)}>
Click me
</button>
</div>
)
}
Copy the code
UseEffect is executed after every DOM rendering, without blocking the page rendering. UseEffect also has the execution timing for componentDidMount, componentDidUpdate, and omponentWillUnmount.
There are also side effects that require additional cleanup when a component is uninstalled, such as subscribing to a feature:
class FriendStatus extends React.Component {
constructor(props) {
super(props);
this.state = { isOnline: null };
this.handleStatusChange = this.handleStatusChange.bind(this);
}
componentDidMount() {
ChatAPI.subscribeToFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
componentWillUnmount() {
ChatAPI.unsubscribeFromFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
handleStatusChange(status) {
this.setState({
isOnline: status.isOnline
});
}
render() {
if (this.state.isOnline === null) {
return 'Loading...';
}
return this.state.isOnline ? 'Online' : 'Offline';
}
}
Copy the code
After subscribing to componentDidMount, we need to unsubscribe from componentWillUnmount. Next we use useEffect in the function component:
import React, { useState, useEffect } from 'react'; function FriendStatus(props) { const [isOnline, setIsOnline] = useState(null); useEffect(() => { function handleStatusChange(status) { setIsOnline(status.isOnline); } ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); // Return a function for additional cleanup: return function cleanup() { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); }; }); if (isOnline === null) { return 'Loading... '; } return isOnline ? 'Online' : 'Offline'; }Copy the code
When useEffect returns a function, React performs a cleanup before the next execution of the side effect. The entire component declaration cycle flow can be understood as follows:
Component mount -> Perform side effects -> Component update -> Perform cleanup function -> Perform side effects -> Component Update -> Perform cleanup function -> Component unload
As mentioned above, useEffect is executed after each render, but sometimes we only want it to be executed when the state and props change
Use in class Component like this:
componentDidUpdate(prevProps, prevState) {
if (prevState.count !== this.state.count) {
document.title = `You clicked ${this.state.count} times`;
}
}
Copy the code
With useEffect, we just pass in the second argument:
useEffect(() => { document.title = `You clicked ${count} times`; }, [count]); // Effect is executed only when count changesCopy the code
When the second argument is an array, you can pass in multiple values, usually passing in all the props and state you need
When side effects only need to be performed at componentDidMount and componentWillUNmount, the second argument can be passed as an empty array []. The implementation benefits are similar to componentDidMount and componentWillUNmount
useLayoutEffect
UseLayoutEffect is used exactly the same as useEffect in that both side effects and cleanups can be performed, with the only difference being when they are performed
UseEffect does not block the browser’s drawing task; it does not execute until the page is updated.
UseLayoutEffect, like componentDidMount and componentDidUpdate, blocks the rendering of a page and causes it to stall if it executes a time-consuming task.
In most cases, useEffect is a good choice. The only exception to this rule is when DOM manipulation is required based on the new UI. UseLayoutEffect ensures that it is performed before rendering the page, i.e. rendering the page to its final effect. If useEffect is used, the page is more likely to jitter after rendering twice.
useContext
UseContext allows us to share variables directly across component hierarchies
The following code allows the count variable to be passed and used across hierarchies (that is, the context is implemented), and the child component changes when the parent component’s count changes:
import React, { useState , createContext } from 'react';
const CountContext = createContext()
function Example(){
const [ count , setCount ] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={()=>{setCount(count+1)}}>click me</button>
<CountContext.Provider value={count}>
</CountContext.Provider>
</div>
)
}
export default Example;
Copy the code
How does a hook component receive this variable
import React, {useState , createContext , useContext} from 'react'; Function Counter(){const count = useContext(createContext) return (<h1>{count}</h1>)}Copy the code
Provider < countContext. Provider> < countContext. Provider> < countContext. Provider>
<CountContext.Provider value={count}>
<Counter />
</CountContext.Provider>
Copy the code
useMemo
Let’s start with an example that doesn’t use useMemo:
import React from 'react'
export default function withoutMemo() {
const [count , setCount] = useState(1);
const [val , setValue] = useState('');
function expensive() {
let sum = 0;
for (let i = 0; i < count * 100; i++){
sum += i;
}
return sum;
}
return (
<div>
<h3>{count} - {val} - {expensive()}</h3>
<div>
<button @click={() => setCount(count + 1)} > +c1 </button>
<input value={val} onChange={event => setValue(event.target.value)} />
</div>
</div>
)
}
Copy the code
Here we create two states, and then we do an expensive calculation using the expensive function to get some value of count, and we can see: Rerendering of either count or val triggers the execution of the expensive component, but the calculation only depends on the value of count. There is no need to re-evaluate val when it changes. In this case, we can use useMemo as follows:
import React from 'react'
export default function withoutMemo() {
const [count , setCount] = useState(1);
const [val , setValue] = useState('');
const expensive = useMemo(() => {
let sum = 0;
for (let i = 0; i < count * 100; i++){
sum += i;
}
return sum;
},[count]);
return (
<div>
<h3>{count} - {expensive()}</h3>
{val}
<div>
<button @click={() => setCount(count + 1)} > +c1 </button>
<input value={val} onChange={event => setValue(event.target.value)} />
</div>
</div>
)
}
Copy the code
As you can see above, useMemo is used to perform expensive calculations and then return the calculated value, passing count in as a dependent value. This only triggers the expensive execution when count changes, and returns the last cached value when val is modified.
useCallback
With useMemo out of the way, useCallback is next. UseCallback is similar to useMemo, but it returns cached functions. Let’s look at the simplest use:
const fnA = useCallback(fnB, [a])
Copy the code
The useCallback above will return the function fnB we passed to it and cache the result; When a dependency changes, the new function is returned. Since the returned function is a function, we can not well determine whether the returned function is changed, so we can use the new data type Set of ES6 to determine, as follows:
import React, { useState , useCallback } from 'react' const set = new set(); export default function Callback() { const [count , setCount] = useState(1); const [val , setValue] = useState(''); const callback = useCallback(() => { console.log(count) },[count]); set.add(callback) return ( <div> <h3>{count}</h3> <h3>{set.size}</h3> <div> <button @click={() => setCount(count + 1)} > +c1 </button> <input value={val} onChange={event => setValue(event.target.value)} /> </div> </div> ) }Copy the code
We can see that set.size is +1 each time we change count. This means that useCallback depends on the variable count. When val changes, set.size does not change, indicating that the old cached version of the function is returned.
What does it do to know what useCallback is?
The usage scenarios are as follows: there is a parent component that contains a child component that receives a function as props; In general, if the parent component updates, the child component also performs the update; In most cases, however, the update is not necessary. We can use useCallback to return a function, and then pass that function as props to the child component; In this way, the child components can avoid unnecessary updates.
import React, { useState , useCallback , useEffect } from 'react'
function Parent() {
const [count , setCount] = useState(1);
const [val , setValue] = useState('');
const callback = useCallback(() => {
return count;
},[count]);
set.add(callback)
return (
<div>
<h3>{count}</h3>
<Child callback={callback} />
<div>
<button @click={() => setCount(count + 1)} > +c1 </button>
<input value={val} onChange={event => setValue(event.target.value)} />
</div>
</div>
)
}
function Child({ callback }) {
const [count , setCount] = useState(() => callback());
useEffect(() => {
setCount(callback());
},[callback]);
return <div> {count} </div>
}
Copy the code
In addition to the examples above, all functions that rely on local state or props to create functions that need to be cached are useCallback scenarios.
useRef
UseRef returns a normal JS object that can store arbitrary data in the current property, just as it did with this, which instantiates the object.
function Counter() {
const [count, setCount] = useState(0);
const prevCountRef = useRef();
useEffect(() => {
prevCountRef.current = count;
});
const prevCount = prevCountRef.current;
return <h1>Now: {count}, before: {prevCount}</h1>;
}
Copy the code
Custom Hooks
A custom Hook is a normal function definition. It is named use to facilitate static code detection.
conclusion
That’s it, Hooks. It takes a lot of effort to understand the Hooks design completely, so hopefully this article has provided you with some help learning about this new feature.