The introduction
This article will introduce some tips on using React hooks in your daily development. It will focus on the use of two hooks: useState and useEffect.
1. In the age of the React Class, why do we use React hooks?
First, let’s be clear about the following points:
- React hooks are completely optional, you can rewrite old redundant components, you can use them in new components, or you can learn to do nothing, like a salt fish 🐟.
- 100% backward-compatible Hooks do not include any destructive < subversive > updates.
- – Class will not be removed, it will still be accompanied by hooks, but officially it is recommended to use hooks,
Because in most cases hooks do what a class can do
.
When using react Class components, it is difficult to reuse state logic across components because we have to implement business logic (such as setState) through various methods mounted on instance object this. Too much “This” can easily lead to confusion among junior developers (such as the question of who this refers to, which also happens in VUe2).
3, complex components become redundant, in the later stages of the project, and the class components are likely to become difficult to maintain, imagine thousands of lines of a component, there will be hundreds of internal function, because each function has its own scope, so if we want to use in these methods the state of props attribute, we have to redefine, The functionComponent feature of hooks prevents this from happening, reducing code duplication and improving efficiency. (It’s hard to say that there are some performance issues associated with this, but for most projects it doesn’t affect performance too much)
This is also one of the advantages of hooks, which are easy for those who know React, and easy for those who do not yet use React, because vue3 is the same in mind design as React hooks. Of course, React has been considered more difficult to learn than VUE for many years, so according to the recruitment ads, other companies use VUE more than famous companies. Hooks made it easier to some extent.
5, hooks to differentiate logic more granular, is the use of hooks can be encapsulated with dom components, not only can encapsulate some public response type logic method (truthfully when listening to return a user login, this state we can reuse in any hooks components), apparently class components in dealing with this kind of thing, It’s a little bit more complicated.
UseState and useEffect
Let’s briefly introduce how useState and useEffect are used
const [demo1Name, setDemo1Name] = useState(' ');
Copy the code
Return a two-entry array, the first of which is the latest value returned by useState, and the second, which can be thought of as a named setState, which we initialize to an empty string by passing useState as the only argument
// Declare multiple state variables
const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: 'learning Hook' }]);
Copy the code
UseEffect Hook (componentDidMount); ComponentDidUpdate and componentWillUnmount.
// Every time componentDidUpdate is triggered
useEffect(() = > {
console.log('I keep triggering useEffect1')})ComponentDidMount is triggered only when the component is initialized
useEffect(() = > {
console.log('I triggered useEffect2 when I initialized.')}, [])// Whenever the nameZyy changes and is initialized, it triggers something similar to vue watch but stronger than watch
useEffect(() = > {
console.log('Because nameZyy changed, I triggered useEffect3')
}, [nameZyy])
// Whenever nameXyb changes and is initialized
useEffect(() = > {
console.log('Because nameXyb changed, I triggered useEffect4')
}, [nameXyb])
// The function componentWillUnmount in return is triggered when the component is destroyed
useEffect(() = >{
return () = > {
// Clear timer, clear event listener function}}, [])Copy the code
Each component update is equivalent to re-executing the componentfunction
Sharing redirects vscode
-
In a class component, an update view operation is usually triggered by the render function and a specific Update lifecycle, and its internal state points to a unique memory address via this instance, thus ensuring consistency of component state after an update.
-
In this case, hooks use methods in React to change this component from stateless to useState. In this case, hooks use methods in React to change this component from stateless to useState. In this case, hooks use methods in React to change this component from stateless to useState. Inside useState is a variable that you defined when you initialized it.
In order to better demonstrate the above theory, I used the concept provided by the netease Cloud team, which is similar to the concept of animation frames (we call each trigger render a frame). It is easier to understand the implementation mechanism of hooks
The variable methods defined in each frame are independent of each other
To explain the concept of frames, here is an example of a timer
Sharing redirects vscode
function Demo2() {
const [num, setNum] = useState(0);
// componentDidMount
useEffect(() = > {
setInterval(() = > {
setNum(num + 1) // num is always 0, I always take the first frame which is the initialization value
console.log(num)
}, 1000);
}, [])
useEffect(() = >{
console.log(num,'I'm going to be a 1, I'm going to stay a 1, I'm not going to trigger render.')
},[num])
return (
<Card title="demo2">
<p>{num}</p>
</Card>)}Copy the code
How does demo2 perform above, first initialize the first frame to simulate the code for the first frame below
function Demo2_1() {
const num_1 = 0;
// componentDidMount
useEffect(() = > {
setInterval(() = > {
setNum(num_1 + 1)
console.log(num_1)
}, 1000);
}, [])
useEffect(() = >{
console.log(num_1,'WHEN I initialize, I execute the following 0. On frame 2, I change to 1. After that, IT stays 1.)
},[num_1])
return (
<Card title="demo2">
<p>{num_1}</p>
</Card>)}Copy the code
The constant num_1 and two useEffect functions are defined inside the Demo2 component, one of which useEffect is executed only on the first frame of initialization. The results are: Set num_1 = 0, setNum = 0, useEffect console.log = num_1 = 1, useEffect console.log = num_1 = 1 It will no longer trigger the render.
Sharing redirects vscode
If you don’t find the above examples convincing, OK! Let me give you an example of netease BIG V
const Demo3 = (props) = > {
const { count } = props;
const handleClick = () = > {
setTimeout(() = > {
alert(count);
}, 3000);
};
return (
<Card title="demo3">
<p>{count}</p>
<button onClick={handleClick}>I play my point</button>
</Card>
);
}
export default Demo3;
Copy the code
We pass count to the child component via props, which starts at 0 and increments by 1 every second. When we click on the button, alert’s count is not the value that is still incrementing on the page, it is the value that is equal to the moment we click. Thus, it proves that the value of each frame is independent, just an ordinary data.
- Of course this only happens in hooks; with the class component, since this refers to the same instance, the internal property value of this keeps changing.
class Demo4 extends Component {
handleClick = () = > {
setTimeout(() = > {
alert(this.props.count);
}, 3000);
};
render() {
const {
count
} = this.props;
return (
<Card title="demo4">
<p>{count}</p>
<Button onClick={this.handleClick}>Click me play me 4</Button>
</Card>); }}Copy the code
summary
In the two examples above, the hooks function redefines internal variables every time Render is triggered, using the concept of animation frames. React, for example, helps us maintain a set of states that are stored independently of the function.
To be clear, the variables we deconstructed from useState as constants in the function are plain data, and there are no data binding operations to force DOM updates. After calling setState in hooks, React will re-execute the Render function and that’s it.
Never cheat useEffect
For useEffect, the time to execute is after all DOM changes have been made and the browser has rendered the page
const Demo5 = (props) = > {
const {
id
} = props;
useEffect(() = > {
setTimeout(() = > {
console.log(id, 'I'm the ID of DEMO5')},100)}, [])return (
<Card title="demo5">I am a demo5 {id}</Card>)}Copy the code
Usually we will have this demand, id is a dynamic returned by the interface, demo5 as a subcomponent he also want to use the id request an other interface, at this time, actually we rely on the dynamic id, but wrote in the subcomponents are unable to get the latest id value, because the child components in the id value, when there is a change will have finished rendering.
We have two solutions to this problem: 1. UseEffect specifies the id dependency in the second parameter array and shorts the protection.
useEffect(() = > {
if (id) {
setTimeout(() = > {
console.log(id, 'I'm the ID of DEMO5')},100)
}
}, [id])
Copy the code
2. Render child components when the ID value changes. But in cases such as wikis where article components are not destroyed, the advantage of using hooks comes into play.
The practice of useMemo
| ![](https://assets.che300.com/wiki/2021-03-29/16170023709952396.png)
|_
Copy the code
import React, { useState } from 'react'
import { Button,Card } from 'antd';
export default() = > {const [count, setCount] = useState(1);
const [val, setValue] = useState('Zhu Yuyi');
function add() {
console.log('I did it again.');
let sum = 0;
for (let i = 0; i < count * 100; i++) {
sum += i;
}
return sum;
}
return (
<Card title="demo6">
<h4>{count}---{add()}</h4>
{val}
<div>
<Button onClick={()= > setCount(count + 1)}>+1</Button>
<input value={val} onChange={event= > setValue(event.target.value)} />
</div>
</Card>
);
}
Copy the code
Running demo6 above, we can see that the add function is executed whenever we click on Button or add a value to input. However, the add function has nothing to do with val, resulting in a performance penalty. So in this case we need to use the useMemo hook.
export default() = > {const [count, setCount] = useState(1);
const [val, setValue] = useState('Zhu Yuyi');
const add = useMemo(() = > {
console.log('I only execute if count changes');
let sum = 0;
for (let i = 0; i < count * 10; i++) {
sum += i;
}
return sum;
}, [count])
return (
<Card>
<h4>{count}-{add}</h4>
{val}
<div>
<Button onClick={()= > setCount(count + 1)}>+c1</Button>
<input value={val} onChange={e= > setValue(e.target.value)} />
</div>
</Card>
);
}
Copy the code
The add function is triggered only when we click the Button, saving performance. Here is a practice diagram from the question bank project
List filters are cached to reduce unnecessary rendering and improve performance. (The original class component didn’t take this into account.)
In the old class component, we used shouldComponentUpdate to determine if the current property and state are the same as the last one to avoid unnecessary updates to the component. The comparison is for all the attributes and states of this component. There is no way to update some elements and not others based on the return value of shouldComponentUpdate. UseMemo in the function component can actually do this job.
Powerful custom hooks
import { useEffect, useState, } from 'react'
export function useOnline() {
const [online, setOnline] = useState(false);
useEffect(() = > {
let timer = setInterval(() = > {
setOnline(c= > {
return! c }) },3000)
return () = > {
clearInterval(timer)
}
}, [])
return online
}
Copy the code
The hooks above are special; they define an online state that changes true false every three seconds to simulate whether the user is online or offline. We can just go back to that state. How do you use it?
export default() = > {const online = useOnline();
console.log(online)
return (
<Card title="demo8">
<div>{online ? 'I'm online' : 'I'm offline '}</div>
</Card>
);
}
Copy the code
In this example, call const online = useOnline() directly; To obtain the response data online. This is where custom hooks are powerful, and smart developers have developed all sorts of hooks that work because of them
How do parent components call child component methods
Use the useRef useImperativeHandle forwardRef in conjunction with the class component
interface useRefTypes {
getTopicList: () = > void
}
const topicRef = useRef<useRefTypes>(null); .const topicDom = (
<div className={styles.describeContainer}>
<Topic
dispatch={dispatch}
subjectId={id}
loading={loading}
userId={_id}
ref={topicRef}
/>
</div>)...// Submit and post comments
const submitAndTopic = () = > {
if(! submitValue) { notification.warning({message: 'tip'.description: 'Please enter your answer'
})
return
}
confirm({
title: 'tip'.content: 'Do you want to synchronize your answers to the comments section? '.okText: 'sure'.cancelText: 'cancel'.onOk: () = > {
submit();
dispatch({
type: 'subjectDetails/addTopic'.payload: {
subjectId: id,
content: submitValue,
parentTopicId: 'topTopic'
},
callback: () = > {
if (topicRef && topicRef.current) {
if(topicRef.current.getTopicList) { topicRef.current.getTopicList(); }}}})}})}Copy the code
Use usRef in parent (top code) and useImperativeHandle forwardRef in child (bottom code)
Wrap the method in the forwardRef
const Topic: React.FC<propsType> = forwardRef((props, ref) = >{... useImperativeHandle(ref,() = > ({
getTopicList
}));
}
Copy the code
Wrap the method in the forwardRef and internally call useImperativeHandle (useImperativeHandle allows you to customize the instance value exposed to the parent component when using the ref. In most cases, imperative code like ref should be avoided. UseImperativeHandle should be used with the forwardRef.
The problem
The questions my studious colleagues discussed with me before the meeting are as follows
Are Hooks slow to create functions at render time?
Don’t. In modern browsers, the raw performance of closures and classes differs significantly only in extreme scenarios. In addition, it can be argued that the Hooks design is more efficient in some ways:
Hooks
To avoid theclass
Additional overhead is required, such as the cost of creating class instances and binding event handlers in constructors.- The language-conforming code is in use
Hooks
Does not require deep component tree nesting. This is common in code bases that use higher-order components, render props, and context. The React component tree is smallThe workload
It also decreases.
We place the code that will only be executed in useEffect inside the useEffect callback so that the useEffect internal function declaration is executed only when the dependent value changes.
2,Use only at the top levelHook
?
Yes yes!! Never call hooks in loops, conditions, or nested functions. Make sure you always call them at the top of your React function. By following this rule, you can ensure that hooks are called in the same order every time you render. This allows React to keep the hook state correct between multiple useState and useEffect calls.
3,useState
The synchronization feature is displayed in the callback function of the interface, i.e., each timesetState
Triggered multiple timesrender
To fix this, use the unstable_batchedUpdates method provided by React, so that two unrelated setstates do not trigger two render sessions, but one.
import { unstable_batchedUpdates as batchedUpdates } from 'react-dom';
dispatch({
type:'xxxx'.callback:() = >{
batchedUpdates(() = >{
// Two different states
setIsMeThumbsUpState(flag= >! flag); setThumbsUpCount(1); })}})Copy the code
Reference documentation
React official Chinese website juejin.cn/post/684490… Blog.csdn.net/qq_44753552… UseEffect and useLayoutEffect addeventlistener: useEffect and useLayoutEffect
other
Lay particular emphasis onuseEffect
Always pay attention to the state value in the event listener callback when writing the event listener in. ifuseEffect
If no dependency is addedstate
Always the initial value