preface
Remember that I haven’t written this article for a while. Here are eight React Hooks that I think I should learn. There should be nine if you include custom Hooks, but I won’t expand on them in this article. I just learned the React framework before I came to netease. After reading the company’s project for one day, I found that I had to read React Hooks. After watching it for a weekend, I made some summaries and shared them here. This article covers each of these Hooks with examples and use scenarios. It is a bit long, so you can look at them purposefully for the Hooks you want to learn.
React Hooks
1 useState
In class 1.1 components
In a class component, you can use this.state to define the state of the class component, as shown in the following code
import React from 'react'
class StateClass extends React.Component{
constructor(){
super(a)this.state = {
name: 'class'}}render() {
return (
<div onClick={ this.setName} >This is a class component ————{this.state.name}</div>
)
}
setName = () = > {
this.setState({
name: 'I'm doing this with the class component method'}}})export default StateClass
Copy the code
1.2 Function Components
In a function component, you can use useState to define the state of the function component. Use useState to create the state
- 1. The introduction of
- 2. Accept a parameter as an initial value
- 3. Return an array with the state as the first value and the state-changing function as the second value
Looking at the same function implementation, the useState code implementation of the function component is as follows. Is it easier to write useState for the same function? Let’s move on to another set of hooks
import React,{ useState } from 'react'
function StateFunction () {
const [name, setName] = useState('function')
// Class name, modify function name initial value
return (
<div onClick={() = >}> // setName can also write methods, such as setName(val => val+' XXXX ') which is a functional component ————{name}</div>)}export default StateFunction
Copy the code
2 useEffect
2.1 Introduction
UseEffect is also called hooks. What it does: Adds an end-of-render signal to a component that has no life cycle. Execution timing: Executed after rendering
-
What are side effects?
- Side effects: Data retrieval, data subscription, and manual changes to the DOM in the React component are all side effects
- Because the page we render is static, anything that happens after it has an effect on it is called a side effect
-
Use:
- 1. The first argument takes a function as an argument
- 2. The second argument, receiving the dependency list, will execute the function only if the dependency is updated
- 3. Return a function, first execute the return function, then execute the parameter function
2.2 The case where the second parameter is not accepted
If the second parameter is not accepted, the useEffect callback will be called after the first render and every time the render page is updated, so you need to consider the scenario.
import React,{ useEffect, useState } from 'react'
function StateFunction () {
const [num, setNum] = useState(0)
useEffect( () = > {
console.log('2222 Functional component end render ')})return (
<div onClick={() = >SetNum (num => num+1)}> This is a functional component ————{num}</div>)}Copy the code
2.3 When the second parameter is accepted
useEffect( () = > {
console.log('2222 Functional component end render ')
},[])
// change the second argument to useEffect
Copy the code
Here, we can pass the second argument an array that represents the list on which the update will be executed. The callback will only be triggered when the list of dependencies changes (useEffect is re-executed when any item in the array changes)
- An empty array is passed in
[]
, then telluseEffect
Don’t depend onstate
,props
Any value of,useEffect
It will only run once, and the common scenario is that the method of getting data for the page can be written here and called to get the initial data for the page - Pass an array of values, or an array of values, as in
[num]
,[num,val]
, the above code is changed to the following. The callback will only be triggered again if any of the values in the array changeuseEffect( () = > { console.log('2222 Functional component end render ') },[num]) useEffect( () = > { console.log('2222 Functional component end render ') },[num,val]) // change the second argument to useEffect Copy the code
2.4 Side effects of removal
These are all side effects that don’t need to be cleaned up. Callbacks trigger simple methods, but there are some side effects that need to be cleaned up. Such as binding DOM events, in which case cleaning is important to prevent memory leaks, such as the code comparison shown below
- 1.Without removing the side effects. Click normal output for the first time
Print current location
“And every timeuseEffect
The call is bound to a new oneupdateMouse
Method, then click on the binding triggered by more and more methods, then click on a crazy printPrint current location
, which leads to page performance, memory leaks, etcconst [positions,setPositions] = useState({ x:0.y:0 }) useEffect( () = > { console.log('2222 Functional component end render ') const updateMouse = (e) = > { console.log('Print current location') setPositions({ x:e.clientX, y:e.clientY }) } document.addEventListener('click',updateMouse) }) return ( <div> <p>x:{ positions.x }</p> <p>y:{ positions.y }</p> </div> ) Copy the code
- 2.Remove side effects in case(Modify only part of the code, the other code as above). Example code
-
The first refresh or entry to the page executes something other than return, that is, a bound method is executed, and then the updateMouse method is bound to the Click event
-
UseEffect is cleared and returned, but the contents of return are not executed.
-
Then when you click the first time, it will print the current mouse page coordinates, and then execute the previous return. Note that this is the clear event binding method of the previous return, and then execute the clear event binding method of the previous useEffect
-
It then executes the new useEffect binding method and returns the useEffect cleanup method again, thus creating a chain of events
-
When the page unloads, the last return is returned to clear the event binding, which ensures that the DOM event method added by the binding is removed when the page unloads
-
(The above written execution process is not analyzed from the principle, just a simple description. It might be a little confusing, but if you don’t understand it, watch it a few more times and run the sample code to make sense.)
useEffect( () = > { console.log('2222 Functional component end render ') const updateMouse = (e) = > { console.log('Print current location') setPositions({ x:e.clientX, y:e.clientY }) } document.addEventListener('click',updateMouse) // Add a binding method event (to modify dependencies, bind to dependencies) return () = > { // useEffect is executed before the last return document.removeEventListener('click',updateMouse) // Remove the binding method event (to modify the dependency, bind to the dependency) console.log('1111 destroyed')}})Copy the code
-
2.5 useEffect Asynchronism
Each effect function belongs to a specific render:
- ①useEffect scheduling does not block browser update screen
- (2) Every time you re-render, a new effect will be generated, replacing the previous one to ensure that the value obtained in Effect is up to date and does not have to worry about expiration. Set as follows
3000
Three clicks in a row in milliseconds will print a total of four times, respectivelyZero, one, two, three
.0
It’s triggered automatically after the first rendering, and the restOne, two, three
It’s three clicks per triggercount
valuefunction Counter() { const [count, setCount] = useState(0); useEffect(() = > { setTimeout(() = > { console.log(`${count}`); }, 3000); }); return ( <div> <p>You hit {count} times</p> <button onClick={()= >SetCount (count + 1)}> Click me</button> </div> ); } Copy the code
- Compare with a class component: If placed in a class component, it is the final value changed within the time set for printing. The equivalent code in a class component is as follows (only the key code is given). What does it mean to change the final value of the print setting over time? If you set it to 3000 milliseconds, then the moment the render ends is timed, and three clicks in a row within 3000 milliseconds will result in 4 prints
3
If I wait for the first timecomponentDidMount
When the timer set in, and then suddenly click three times in a row within 3000 milliseconds, the first one will be printed first0
Print it three more times3
.Because it’s the same thing in the class notationnum
The status value. If you set the time to0
Millisecond, so actually if you click three times in a rowuseEffect
Once again, print it first0
“, and then printOne, two, three
Because this change is quick and you won’t feel the difference, you can try it yourself.this.state = { num:0 } componentDidMount(){ setTimeout(() = > { console.log(this.state.num) },3000)}componentDidUpdate(){ setTimeout(() = > { console.log(this.state.num) },3000)}render() { return ( <div onClick={ this.setNum} >This is a class component ————{this.state.num}</div> ) } setNum = () = > { this.setState({ num: this.state.num+1})}Copy the code
- Compare with a class component: If placed in a class component, it is the final value changed within the time set for printing. The equivalent code in a class component is as follows (only the key code is given). What does it mean to change the final value of the print setting over time? If you set it to 3000 milliseconds, then the moment the render ends is timed, and three clicks in a row within 3000 milliseconds will result in 4 prints
3 useLayoutEffect
UseLayoutEffect is commonly referred to as a side effect of DOM manipulation hooks. The function is to perform an operation after a DOM update is complete. Execution timing: Execute after DOM update
Compared with useEffect
- The same
- 1. The first argument takes a function as an argument
- 2. The second argument, receiving the dependency list, will execute the function only if the dependency is updated
- 3. Return a function, first execute the return function, then execute the parameter function
- (So the execution process is the same)
- The difference between
- Execution timing varies.
useLayoutEffect
inDOM
Execute after update;useEffect
inrender
Execute after rendering. You’ll see by executing the sample codeuseLayoutEffect
Than everuseEffect
Execute first. That’s becauseDOM
After the update, the rendering will finish or will finish
- Execution timing varies.
const [num, setNum] = useState(0)
// Implement the componentWillMount life cycle in the class component
useLayoutEffect( () = > {
console.log('useLayoutEfffect')
// Event binding can also be done here
return () = > {
// Event binding removal can also be done here
console.log(1)
}
},[num])
useEffect( () = > {
console.log('useEffect')
},[num])
return (
<div onClick={() = >SetNum (num => num+1)}> This is a functional component ————{num}</div>
)
Copy the code
4 useMemo
Using useMemo, you can pass a create function and a dependency. The create function returns a value, and only when the dependency changes will the function be called again to return a new value. Simply put, the effect is to make the functions in the component follow state updates (that is, function functions in the optimization function component).
- Use:
- 1. Accept a function as an argument
- 2. Also accept the second parameter as the dependency list (can be compared with useEffect and useLayoutEffect)
- 3. A value is returned. The return value can be any function, object, etc
4.1 Optimization scenarios of complex Computing Logic
- The code before optimization is as follows. When we click
div
Region is triggered at this timesetAge
What has changed is thatage
, withgetDoubleNum
The method is actually irrelevant, but if you look at the console, you can see that it prints multiple timesGet double Num
, indicating that the method is constantly fired, which is not necessary. If there is a large amount of calculation in the method, it will have a certain impact on performance, so it needs to be optimizedconst [num, setNum] = useState(1) const [age, setAge] = useState(18) function getDoubleNum () { console.log('gets double Num${num}`) return 2 * num // Assume complex computing logic } return ( <div onClick={() = > { setAge( age => age+1 )} }> <br></br>This is a functional component ————{getDoubleNum()}<br></br>The age value is ————{age}<br></br> </div> ) Copy the code
- use
useMemo
The optimized code is as follows. At this timegetDoubleNum
The method is to receive a value that is returned, so notice in the comments that the parentheses are removed. useuseMemo
After, click againdiv
Regional changeage
Is returned when executedreturn 2*num
As well as printing only innum
Update, and return the value togetDoubleNum
Rendering to the view reduces unnecessary computation and optimizesconst [num, setNum] = useState(1) const [age, setAge] = useState(18) const getDoubleNum = useMemo( () = > { console.log('gets double Num${num}`) return 2 * num // Assume complex computing logic },[num] ) return ( <div onClick={() = > { setAge( age => age+1 ) } }> <br></br>This is a functional component ————num: {getDoubleNum} // Note that there are no parentheses because this is a return value<br></br>The age value is ————{age}<br></br> </div> ) Copy the code
4.2 The problem of repeated rendering of parent and child components to optimize the use of the scene
- The code before optimization is as follows. The child component wraps one
memo
, but the package will still be re-rendered, why? Because we defined itinfo
isconst
A local variable is defined, and a new one is redefined each time it is rerenderedinfo
And then when the sub-components perform shallow comparisons,info
It will always be different, so it will be re-rendered (you can follow the example by clicking the button, and you will see that the child component keeps printingI'm a child component
). If the subcomponents are complex, this can have an impact on page performanceconst Child = memo( () = > { console.log('I'm a child component') return <p>I'm a child component</p> }) function Parent() { const [show,setShow] = useState(true) const info = { name: 'Even'.age: 22 } return( <div> <Child info={ info} / > <button onClick={() = >setShow(! }> Click to update the status</button> </div>)}Copy the code
- use
useMemo
The following code is shown (only the modified code is given, the other code is the same as the above example). Optimized this way, the child component will only render once in the initialization state when we click the button becauseinfo
The parceluseMemo
The dependency does not change and the return value is the same, so there is no re-rendering of the child component.const info = useMemo( () = > { return { name: 'Even'.age: 22 } },[]) Copy the code
5 useCallback
A similar one is called useCallback, which allows certain operations and methods to be executed following state updates.
Contrast with useMemo.
- And the simple way to think about it is,
useMemo(() => Fn,deps)
The equivalent ofuseCallback(Fn,deps)
Difference:
useCallback
Is optimized for the callback function passed, and returns a function;useMemo
The return value can be anything, function, object, etc
Similarities:
- In terms of the method of use,
useMemo
withuseCallback
The same. Take a function as an argument, and also take a second argument as a dependency list
5.1 why saiduseCallback
Caching is a function (important difference)
-
UseCallback, while similar to useMemo, returns and caches a function, as compared to the following example code. ①②③ Comparison of the three cases (you can copy the code and comment the code comparison respectively)
-
In the ① case, it will print only once to get double Num1, i.e. the first rendering, and then click on the div area to change the age value, so it will not execute. Since getDoubleNum already gets the value returned by the function passed in useMemo, it caches it
-
In the case of ②, the first rendering will print for a double Num1, and then each click will print for a double Num1. Why? Doesn’t useCallback also have caching capabilities? This is because, as we mentioned earlier, useCallback returns a function. Because the useCallback function is defined in the current component, the component is re-rendered, and it will also be re-rendered. If the dependence of ③ is [], then you will understand. Therefore, scenarios with complex computational logic are not suitable for caching using useCallback because the incoming function content is constantly being executed.
-
In the case of ③, we combine the marking code at ②③, and set can only store a unique value. We observe the length of the printed set
- when
useCallback
Rely on is empty[]
“, we click multiple times in a rowdiv
Region, althoughuseCallback
The content is executed continuously, but we can see it printed outset
The length of omega is always omega2
That’s because it keeps adding the same functionset
, soset
The length of phi is constant - And when the
useCallback
Rely on for[num]
“, we click multiple times in a rowdiv
Area, you can see it printed outset
It keeps adding up,One, two, three, four, five, six...
. becausenum
Is changing, so every time the cached function is a new function, so add inset
The functions of phi are different, soset
The length of the dot is added again and again
- when
const set = new Set(a)export default function StateFunction () { const [num, setNum] = useState(1) const [age, setAge] = useState(18) const getDoubleNum = useMemo( () = > { console.log('gets double Num${num}`) return 2 * num // },[] ) const getDoubleNum = useCallback( () = > { console.log('gets double Num${num}`) return 2 * num // 2 },[] ) set.add(getDoubleNum()) // Set the dependency of Callback to [] and [num]. console.log('set. The size:,set.size) return ( <div onClick={() = > { setNum( num => num+1 ) } }> <br></br>This is a functional component ————num: {getDoubleNum} //① In the useMemo case this is a functional component ————num: {getDoubleNum()} //② in the useCallback case<br></br>The age value is ————{age}<br></br> </div>)}Copy the code
-
5.2 Application Scenario of the useCallback
It is possible to optimize the issue of parent-child component parameter rendering. Simply put, if the parent’s passed function is not updated, the child’s function will not be reexecuted
-
In general, when the parent component is updated, the child component is also updated. But if what the parent passes to the child doesn’t change, then some of the child’s actions (actions that need to be synchronized with changes in the incoming content) don’t need to be performed, which can affect page performance, so we can optimize this situation.
-
For example, we pass getDoubleNum to the child component, click on the div field to change the value of num, we use the parent component useCallback with the child component useEffect to optimize. Only when the parent component’s num changes and the getDoubleNum passed in to the child component changes will we perform some of the child component’s operations that need to be updated (i.e., comment the code at the annotation point), so as to avoid repeated unnecessary update operations of the child component that affect page performance
function Parent () {
const [num, setNum] = useState(1)
const [age, setAge] = useState(18)
const getDoubleNum = useCallback( () = > {
console.log('gets double Num${num}`)
return 2 * num
},[num] )
return (
<div onClick={() = >{setNum(num => num+1)}}> This is a functional component ————num:{getDoubleNum()}<br></br>The age value is ————age:{age}<br></br>
set.size:{set.size}
<Child callback={ getDoubleNum() }></Child>
</div>)}function Child(props) {
useEffect( () = > {
console.log('Callback updated') // This represents operations that need to be synchronized with changes in the incoming content
},[props.callback])
return (
<div>Subcomponent getDoubleNum{props. Callback}</div>)}Copy the code
5.3 Summarize and refer to the article in detail
Simple summary of use scenario judgment:
- Only in cases where the child component does not need the parent component’s values and functions
memo
The function wraps the child component - If a function is passed to a child component, use
useCallback
- Caches when complex computational logic within a component needs to return values
useMemo
- If a value is passed to a child component, use
useMemo
Refer to the article:
- The above is my personal understanding based on the reference materials. If you think what I said is still not clear, you can refer to these two well-written articles, perhaps it will be clearer
- UseMemo and useCallback Usage Guide
- UseCallback & useMemo
6 useRef
Simply put, useRef returns an index of child elements that remains constant throughout the life cycle. That’s what it does: Keep data for a long time. Note That the saved object is changed without notice. Property changes are not re-rendered
- Don’t use
useRef
If we had a requirement that a timer should be cleared when its increment reaches a limit, we would use the following code. At this point, the following code is actually unable to complete the given requirements, whennum
Is greater than10
After, will find non-stop printingOver 10, clear timer
In fact, the timer is not cleared, so it will always execute the two printed content, but will find the printed contenttimer
According toundefined
Why is that? Because we’re passing every time we rendersetInterval
returnabletimer
.timer
It’s updated, and it’s losttimer
This data makes it impossible to clear a timer that needs to be clearedconst [num, setNum] = useState(0) let timer useEffect( () = > { timer = setInterval( () = > { setNum( num= > num+1)},400 ) },[] ) useEffect( () = > { if(num > 10) {console.log('Greater than 10, clear timer') console.log('the timer:,timer) // Each timer is independently render, so we can't get it clearTimeout(timer) } },[num] ) return ( <div>This is a functional component ————num:{num}</div> ) Copy the code
- use
useRef
After, the code is as follows. We can see thatnum
Since the toAfter 11
I printed it onceOver 10, clear timer
As well asref.current 1
, and then it stops incrementing because the timer is cleared.ref
Is an object,ref.current
Stores the entire life cycle of the timerid
Value, so when the timer is cleared, the timer can be cleared exactly- Save a value that remains constant throughout its life cycle
const [num, setNum] = useState(0) const ref = useRef() useEffect( () = > { ref.current = setInterval( () = > { setNum( num= > num+1)},400 ) // ref.current = '111' },[] ) useEffect( () = > { if(num > 10) {console.log('Greater than 10, clear timer') console.log('ref.current',ref.current) clearTimeout(ref.current) } },[num] ) return ( <div>This is a functional component ————num:{num}</div> ) Copy the code
- To assign a value
ref.current
Page re-rendering is not actively triggered. When we change the code to look like this, we print the discovery on the consoleref.current
Is printed as111
, but the page view is still empty becauseref
Changes to saved objects are not actively notified, and property changes are not re-rendered
const [num, setNum] = useState(0) const ref = useRef() useEffect( () = > { ref.current = '111' console.log('ref.current',ref.current) },[] ) return ( <div>This is the value of ref. Current -- ref. Current :{ref. Current}<br></br>This is a functional component ————num:{num}</div> ) Copy the code
7 useContext
UseContext allows state passed in by the parent to be shared between child components. Function is colloquially referred to as wandering with child components.
- Don’t use
useContext
, we have the following scenario where our parent component has a value passed to a different child component. The code shown in the example is2
But what if I need to add too many children? You can’t always add and write one by one, and if the same variable name is passed in and it changes, you have to change it one by one, so we can useuseContext
Optimize your codefunction StateFunction () { const [num, setNum] = useState(1) return ( <div> <button onClick={() = >SetNum (num => num+1)}> Increment the value of num+1</button> <br></br>This is a functional component -- num:{num}<Item1 num={num}></Item1> <Item2 num={num}></Item2>/ /...</div>)}function Item1 (props) { return ( <div>Subcomponent 1 num: {props. Num}</div>)}function Item2 (props) { return ( <div>Subcomponent 2 num: {props. Num}</div>)}Copy the code
- use
useContext
After optimization, the code looks like this so that we only need to use it in child componentsHandle useContext (Context)
To obtain the data, add the same type of child components do not need to pay attention to the parent component of the subcomponent definitionprops
Pass in the value as follows- Need to introduce
useContetx
.createContext
Two content - through
createContext
Create a context handle Context.Provider
To determine the scope of data sharing- through
value
To distribute content - In the child component, pass
Handle useContext (Context)
To get the data - Matters needing attentionThe upper level data changes, which will definitely trigger rerendering (click
button
Button triggers the parent component to update the incomingnum
Value to see child components re-rendered)
const Context = createContext(null) function StateFunction () { const [num, setNum] = useState(1) return ( <div> <button onClick={() = >SetNum (num => num+1)}> Increment the value of num+1</button> <br></br>This is a functional component -- num:{num}<Context.Provider value={num}> <Item3></Item3> <Item4></Item4> </Context.Provider> </div>)}function Item3 () { const num = useContext(Context) return ( <div>Subcomponent 3: {num}</div>)}function Item4 () { const num = useContext(Context) return ( <div>Subcomponent 4: {num+2}</div>)}Copy the code
- Need to introduce
8 useReducer
Redux can now be used in functional components using useReducer instead of class components. The purpose is to obtain the desired state from the state management tool.
- How to use
useReducer
.Redux
The must-have is the warehousestore
And managersreducer
. whileuseReducer
Again, you need to create a data warehousestore
And managersreducer
, in the sample code comments. And then we can go through1.
Defines an array of actions to get the state and change the state, passed in when the action is triggeredtype
Type judgments are triggeredreducer
Which action, and then modify the data. The important thing to note is that inreducer
In thereturn
Object that needs to be converted tostate
Deconstruct, otherwise there’s only one state leftnum
The value ofconst store = { age:18.num:1 } // Data warehouse const reducer = (state, action) = > { switch(action.type){ case 'add': return { ...state, num: action.num+1 } default: return { ...state } } } / / manager function StateFunction () { const [state,dispacth] = useReducer(reducer,store) / / 1. return ( <div> <button onClick={() = >{dispacth({type: 'add', num: state.num})}}> Increment the value of num +1</button> <br></br>This is a functional component -- num:{state.num}</div>)}Copy the code
conclusion
Are you done? I don’t know if you have any gain after reading this article is analyzed from the use level and some daily application examples, and there is no deep analysis. If you want to learn more about these Hooks, go to the official source code. Learn from React Hooks learn from React Hooks
At the end of the article
If you think my writing is good, you can give me a thumbs up. If there is anything wrong or bad, please comment and point it out so that I can correct it