π οΈ preface
React 16.8 has a very powerful feature called React hooks. In fact, React Hooks are similar to vue3’s Composition API in that they are designed to improve development efficiency.
So, in the following article, we will take you through 0 to 1 to learn about React hooks and some of the commonly used apis.
Without further ado, react-Hook is on its way
1. π» Overview
1. React Hooks
React Hooks
Is an optional feature, usually usedclass
Component to compare to it;100%
Backward compatibility, noDestructive modification;- Won’t replace
class
Component, no plans to removeclass
Components.
Remember remember remember Hooks
(1) Review React functional components
Let’s start with a review of class and function components as follows:
The class components:
/ / class components
class List extends React.Component {
constructor() {
super(props)
}
render() {
const { List } = this.props
return <ul>{list.map((item, index) => {
return <li key={item.id}>
<span>{item.title}</span>
</li>
})}</ul>}}Copy the code
Function components:
// Function components
function List(props) {
const { list } = props
return <ul>{list.map((item, index) => {
return <li key={item.id}>
<span>{item.title}</span>
</li>
})}</ul>
}
Copy the code
(2) Features of function components
Function components are characterized by:
- No component instance;
- No life cycle;
- There is no
state
εsetState
, can only receiveprops
γ
(3) Class component problems
Above we said that the function component is a pure function that only accepts props and does nothing else. The class component does this, but the class component has the following problems:
- Large components are difficult to break down and refactor, and difficult to test (i.e
class
Not easy to split); - The same business logic is scattered into various methods, resulting in logic chaos.
- Reuse logic becomes complex, such as
Mixins
γHOC
γRender Props
γ
So, with these issues, there are React Hooks.
(4) React component
-
The React component is easier to express as a function:
-
React advocates functional programming, that is, view=fn(props);
-
Functions are more flexible, easier to split, and easier to test;
-
But the function component was too simple and needed more power — hence React Hooks.
πͺ several Hooks
1, the State Hook π οΈ
(1) Let function components implement state and setState
- The default function component is none
state
; - The function component is a pure function that is destroyed upon execution and cannot be stored
store
οΌ - Need to be
State Hook
, i.e., thestore
Functions “hook” into pure functions.
(2) Illustrate with examples
Suppose we now want to modify some value by clicking on a button. Now let’s deal with useState in hooks. The specific code is as follows:
import React, { useState } from 'react'
function ClickCounter() {
// Array destruct
// useState is a Hook
const [count, setCount] = useState(0) // Pass in an initial value. The initial value can be a number/string/array/object, etc
const [name, setName] = useState(Monday Lab)
// const arr = useState(0)
// const count = arr[0]
// const setCount = arr[1]
function clickHandler() {
setCount(count + 1)
setName(name + '2021')}return <div>
<p>You clicked {count} times {name}</p>
<button onClick={clickHandler}>Click on the</button>
</div>
}
export default ClickCounter
Copy the code
The browser displays the following information:
In the above code, count is a value of state, and setCount is a function that modifies state. Similarly, name is a state value, and setName is a function that changes the name value.
If we use hooks to fix state, we need to fix state in the form of const [count, setCount] = useState(0), without having to use them in the class component.
For the above functionality, if implemented using the class component, the code is as follows:
import React from 'react'
class ClickCounter extends React.Component {
constructor() {
super(a)/ / define the state
this.state = {
count: 0.name: Monday Lab
}
this.clickHandler = this.clickHandler.bind(this)}render() {
return <div>
<p>You clicked {this.state.count} times {this.state.name}</p>
<button onClick={this.clickHandler}>Click on the</button>
</div>
}
clickHandler() {
/ / modify state
this.setState({
count: this.state.count + 1.name: this.state.name + '2021'}}})export default ClickCounter
Copy the code
As you can see, if you use the class component, you need to define state first, then define a function, and then use setState to change the value, which seems a bit cumbersome.
I believe that by now you have sensed the delight of hooks.
Below, let’s summarize some knowledge points about useState.
(3) useState use summary
useState(xxx)
Pass in the initial value and return the array[state, setState]
οΌ- through
state
Get the value; - through
setState(xxx)
Modify the value.
(4) Hooks naming specification
- Stipulate all
Hooks
useuse
At the beginning, such asuseXxx
οΌ - The custom
Hook
Also want touse
At the beginning. - non
Hooks
Try not to use ituseXxx
It’s easy to misunderstand.
2, Effect Hook π οΈ
(1) Let the function component simulate the life cycle
- The default function component has no life cycle;
- The function component is a pure function, which is destroyed immediately after execution and cannot realize its own life cycle.
- use
Effect Hook
Hook the lifecycle into a pure function.
(2) Illustrate with examples
Similarly, let’s use the same example with useState to experience the useEffect.
We first in SRC | components to set up a file, named LiftCycles. Js. The specific code is as follows:
import React, { useState, useEffect } from 'react'
function LifeCycles() {
const [count, setCount] = useState(0)
const [name, setName] = useState(Monday Lab)
// Simulate DidMount and DidUpdate for class components
useEffect(() = > {
console.log('Send an Ajax request here')})function clickHandler() {
setCount(count + 1)
setName(name + '2020')}return <div>
<p>You clicked {count} times {name}</p>
<button onClick={clickHandler}>Click on the</button>
</div>
}
export default LifeCycles
Copy the code
Now we register the component in app.js. The specific code is as follows:
import React, { useState } from 'react';
import LifeCycles from './components/LifeCycles'
function App() {
const [flag, setFlag] = useState(true)
return (
<div
{flag && <LifeCycles/>}
</div>
);
}
export default App;
Copy the code
The browser displays the following information:
As you can see, useEffect emulates both DidMount and DidUpdate lifecycle functions if passed in only one function. Every time we click, the browser prints once, meaning that component loading and component updating are done together.
If we want to separate component loading and component updating, we can do that. Let’s change the LiftCycles. Js code. The specific code is as follows:
import React, { useState, useEffect } from 'react'
function LifeCycles() {
const [count, setCount] = useState(0)
const [name, setName] = useState(Monday Lab)
// Simulate DidMount of the class component
useEffect(() = > {
console.log('Load done')}, [])// The second argument is [] (independent of any state)
// Simulate the DidUpdate of the class component
useEffect(() = > {
console.log('Updated')
}, [count, name]) // The second argument is the state of the dependency
function clickHandler() {
setCount(count + 1)
setName(name + '2020')}return <div>
<p>You clicked {count} times {name}</p>
<button onClick={clickHandler}>Click on the</button>
</div>
}
export default LifeCycles
Copy the code
The browser displays the following information:
As you can see, the first time I loaded it and updated it once. By the time we click, it’s already loaded, so it’s an update.
UseEffect is used when loading and updating.
UseEffect can also take a second parameter, which is null and does not depend on any state, indicating the lifetime of componentDidMount. Conversely, the second parameter emulates the componentDidUpdate life cycle if it does not depend on a value or receives a value dependent on state.
At this point, some of you may also want to ask, what about the life cycle related to destruction? Let’s go ahead and modify the LiftCycles. Js code. Details are as follows:
import React, { useState, useEffect } from 'react'
function LifeCycles() {
const [count, setCount] = useState(0)
const [name, setName] = useState(Monday Lab)
// Simulate DidMount of the class component
useEffect(() = > {
let timerId = window.setInterval(() = > {
console.log(Date.now())
}, 1000)
// Return a function
/ / simulation WillUnMount
return () = > {
window.clearInterval(timerId)
}
}, [])
function clickHandler() {
setCount(count + 1)
setName(name + '2020')}return <div>
<p>You clicked {count} times {name}</p>
<button onClick={clickHandler}>Click on the</button>
</div>
}
export default LifeCycles
Copy the code
You can see the above code, where we return a function to simulate the life cycle of componentWillUnMount, to destroy the task executed in each timer.
Here, I believe that you have a certain understanding of the useEffect. Now, let’s summarize the useEffect.
(3) useEffect summary
- simulation
componentDidMount - useEffect
Rely on[]
οΌ - simulation
componentDidUpdate - useEffect
No dependency, or dependence[a, b]
οΌ - simulation
componentWillUnMount - useEffect
δΈReturn a function.
(4) useEffect makes the pure function have side effects
- By default, when a pure function is executed, it only needs to enter parameters and return the result, with no side effects.
- Side effects are caused outside the function, such as setting a global scheduled task.
- Components require side effects, so they do
useEffect
“Hook” into pure functions; - As a result,
useEffect
This side effect is not a bad thing.
(5) useEffect returns function fn
One notable case is that we simulated WillUnMount by returning a function above, but the simulated result is not quite equal to WillUnMount. Now, we are in the SRC | components to set up a file, named FriendStatus. Js. The specific code is as follows:
import React, { useState, useEffect } from 'react'
function FriendStatus({ friendId }) {
const [status, setStatus] = useState(false)
/ / DidMount and DidUpdate
useEffect(() = > {
console.log('Start listening${friendId}Online status')
// γ special attention γ
// This is not exactly the same as WillUnMount
// A change in props, that is, an update, is also performed to end the listen
// To be exact: the returned function will be executed before the next effect execution
return () = > {
console.log('End monitoring${friendId}Online status')}})return <div>Friend {friendId} Online status: {status.tostring ()}</div>
}
export default FriendStatus
Copy the code
The code for app.js is as follows:
import React, { useState } from 'react';
import FriendStatus from './components/FriendStatus'
function App() {
const [flag, setFlag] = useState(true)
const [id, setId] = useState(1)
return (
<div>
<div>
<button onClick={()= > setFlag(false)}>flag = false</button>
<button onClick={()= > setId(id + 1)}>id++</button>
</div>
{flag && <FriendStatus friendId={id}/>}
</div>
);
}
export default App;
Copy the code
The browser displays the following information:
And as you can see, when you start the next listener, you end the previous listener before you start the next one. As you can see in the picture, at first we are listening to friend 1, then when we want to click id++ button to listen to friend 2, useEffect will end the status of friend 1 first, and then let friend 2 online.
One point to note at this point is that executing the function that ends the listening executes the DidUpdate life cycle, not the WillUnMount life cycle. The function is in the update state, not the destroy state.
Based on the above, let’s make a summary. Details are as follows:
- when
useEffect
Depends on the[]
Is executed when the component is destroyedreturn
The function offn
Is equal toWillUnMount
Life cycle; - when
useEffect
To be free from or dependent on[a, b]
The component is executed at update timereturn
The function offn
; That is, at the next executionuseEffect
Before, it will be executed firstfn
, and the simulation isDidUpdate
Life cycle.
3. HooksποΈ
Hooks β useState and useEffect. Next, let’s look at a few other hooks that are less commonly used.
(1) useRef
Ref is used for modifying values and retrieving DOM elements. Let’s start with some code:
import React, { useRef, useEffect } from 'react'
function UseRef() {
const btnRef = useRef(null) / / initial value
const numRef = useRef(0)
numRef.current = 2;
useEffect(() = > {
console.log(btnRef.current) / / the DOM node
console.log(numRef.current); / / value
}, [])
return <div>
<button ref={btnRef}>click</button>
</div>
}
export default UseRef
Copy the code
In this case, the browser displays:
As you can see, if you bind btnRef to ref={btnRef}, then btnref. current gets the current DOM node.
The other case is where you locate numRef, and if we assign and modify it, numref.current will end up with the modified value.
(2) useContext
Most of the time, we often have some static properties need to be switched, such as: theme color switch. That’s when you need to use the context context. In a hook, there is useContext that can handle this. Let’s start with some demo code:
import React, { useContext } from 'react'
// Theme color
const themes = {
light: {
foreground: '# 000'.background: '#eee'
},
dark: {
foreground: '#fff'.background: '# 222'}}/ / create the Context
const ThemeContext = React.createContext(themes.light) / / initial value
function ThemeButton() {
const theme = useContext(ThemeContext)
return <button style={{ background: theme.background.color: theme.foreground}} >
hello world
</button>
}
function Toolbar() {
return <div>
<ThemeButton></ThemeButton>
</div>
}
function App() {
return <ThemeContext.Provider value={themes.dark}>
<Toolbar></Toolbar>
</ThemeContext.Provider>
}
export default App
Copy the code
The browser displays the following information:
Now it’s a theme on a black background. If we want to switch to a theme with a white background, we can just change the bottom code value={themes.dark} to value={themes.light}.
(3) the useReducer
In redux, we useReducer, but there is a difference between useReducer and redux. UseReducer states individual components
Let’s start with some code:
import React, { useReducer } from 'react'
const initialState = { count: 0 }
const reducer = (state, action) = > {
switch (action.type) {
case 'increment':
return { count: state.count + 1 }
case 'decrement':
return { count: state.count - 1 }
default:
return state
}
}
function App() {
Const [count, setCount] = useState(0)
const [state, dispatch] = useReducer(reducer, initialState)
return <div>
count: {state.count}
<button onClick={()= > dispatch({ type: 'increment' })}>increment</button>
<button onClick={()= > dispatch({ type: 'decrement' })}>decrement</button>
</div>
}
export default App
Copy the code
The browser displays the following information:
The values we will change are put into the Reducer, which we then pass to the Reducer to useRouter. Finally, the values are bound through dispatch.
Let’s tease out the differences between useRuducer and redux:
useReducer
ζ―useState
For processingstate
Complex changes of;useReducer
ζ―Individual component state managementComponent communication is also requiredprops
οΌredux
Is a globalState management.Multiple components share data.
(4) useMemo
UseMemo is part of the performance tuning in hooks. When we’re not using useMemo, the state looks like this. Take a look at this code:
import React, { useState } from 'react'
/ / child component
function Child({ userInfo }) {
console.log('Child render... ', userInfo)
return <div>
<p>This is Child {userInfo.name} {userInfo.age}</p>
</div>
}
/ / the parent component
function App() {
console.log('Parent render... ')
const [count, setCount] = useState(0)
const [name, setName] = useState(Monday Lab)
const userInfo = { name, age: 20 }
return <div>
<p>
count is {count}
<button onClick={()= > setCount(count + 1)}>click</button>
</p>
<Child userInfo={userInfo}></Child>
</div>
}
export default App
Copy the code
At this point, the console prints as follows:
As you can see, the Child component is reprinted every time we click, regardless of whether the value of state is updated. This is performance – intensive for the program. Therefore, we use useMemo to prevent Child events from being printed frequently and improve the performance of the program. The code modification is as follows:
import React, { useState, memo, useMemo } from 'react'
/ / child component
// Similar to class PureComponent, shallow comparison of props
const Child = memo(({ userInfo }) = > {
console.log('Child render... ', userInfo)
return <div>
<p>This is Child {userInfo.name} {userInfo.age}</p>
</div>
})
/ / the parent component
function App() {
console.log('Parent render... ')
const [count, setCount] = useState(0)
const [name, setName] = useState(Monday Lab)
// Use useMemo to cache data depending on [name]
{name, age: 18} the cache is invalidated when name is updated
const userInfo = useMemo(() = > {
return { name, age: 18 }
}, [name])
return <div>
<p>
count is {count}
<button onClick={()= > setCount(count + 1)}>click</button>
</p>
<Child userInfo={userInfo}></Child>
</div>
}
export default App
Copy the code
The browser displays the following information:
As you can see, the Child component will not print unless it is updated for the first time. First, we use the memo to wrap the subcomponents. Then, we use useMemo to cache the data, and this data depends on [name]. It is worth noting that this does not work if you do not rely on [name].
Now, let’s summarize useMemo as follows:
React
It’s updated by defaultAll child components;class
Components useshouldComponentUpdate
εPureComponent
Do optimization;Hooks
The use ofuseMemo
Do optimization, but optimization principle andclass
The components are the same.
(5) useCallback
We talked about using useMemo to cache data. Now let’s talk about useCallback. UseCallback is mainly used to cache functions.
Assuming we don’t use useCallback now, run the following code:
import React, { useState, memo, useMemo, useCallback } from 'react'
// The memo equals PureComponent
const Child = memo(({ userInfo, onChange }) = > {
console.log('Child render... ', userInfo)
return <div>
<p>This is Child {userInfo.name} {userInfo.age}</p>
<input onChange={onChange}></input>
</div>
})
/ / the parent component
function App() {
console.log('Parent render... ')
const [count, setCount] = useState(0)
const [name, setName] = useState(Monday Lab)
// Use useMemo to cache data
const userInfo = useMemo(() = > {
return { name, age: 21 }
}, [name])
function onChange(e) {
console.log(e.target.value)
}
return <div>
<p>
count is {count}
<button onClick={()= > setCount(count + 1)}>click</button>
</p>
<Child userInfo={userInfo} onChange={onChange}></Child>
</div>
}
export default App
Copy the code
The browser displays the following output:
As you can see, if useCallback is not used, it will always print out the child components, that is, the onChange event will always run. Now, let’s transform the onChange event as follows:
// Use the useCallback cache function
const onChange = useCallback(e= > {
console.log(e.target.value)
}, [])
Copy the code
Now let’s take a look at what the browser looks like:
Add the useCallback and the Child is not updated.
Here’s a summary of useMemo and useCallback:
useMemo
Cache data;useCallback
Cache function;- Both of them
React Hooks
Is a common optimization strategy.
4. Custom HookποΈ
(1) Why use custom Hook
- Used to encapsulate common functions;
- canThe development ofanduseThe third party
Hooks
οΌ - The custom
Hook
Brings infinite extensibility and decouples code.
(2) Illustrate with examples
Suppose we now wanted to encapsulate a useAxios, how would we do that?
First, we need to install AXIOS in our project as follows:
npm i axios --save
Copy the code
Then, we in the project of SRC | customHooks folder to create a new file, named useAxios. Js. The specific code is as follows:
import { useState, useEffect } from 'react'
import axios from 'axios'
// Encapsulates a custom Hook for AXIos to send network requests
function useAxios(url) {
// Loading Simulates whether to wait
const [loading, setLoading] = useState(false)
// The data returned when the request succeeds
const [data, setData] = useState()
// Some information returned when the request failed
const [error, setError] = useState()
useEffect(() = > {
// Use AXIos to send network requests
setLoading(true)
axios.get(url) // Send a get request
.then(res= > setData(res))
.catch(err= > setError(err))
.finally(() = > setLoading(false))
}, [url])
return [loading, data, error]
}
export default useAxios
Copy the code
Go on, we in the project of SRC | components folder, create a new file, named CustomHookUsage. Js. This component is mainly used to use the useAxios hook as defined above. The specific code is as follows:
import React from 'react'
import useAxios from '.. /customHooks/useAxios'
function App() {
const url = 'http://localhost:3000/'
// Array destruct
const [loading, data, error] = useAxios(url)
if (loading) return <div>loading...</div>
return error
? <div>{JSON.stringify(error)}</div>
: <div>{JSON.stringify(data)}</div>
}
export default App
Copy the code
Finally, we’ll use it in our project’s app.js. The specific code is as follows:
import React, { useState } from 'react';
import CustomHookUsage from './components/CustomHookUsage'
function App() {
return (
<div>
{<CustomHookUsage/>}
</div>
);
}
export default App;
Copy the code
At this point, let’s take a look at the browser display:
As you can see, loading occurs first while waiting. After that, if the wait is over and the request is successful, data is returned. Of course, we are not demonstrating the error case here. You can test the error case by changing the URL to a request address that does not exist.
(3) Summary
After looking at the demo code above, let’s make a summary:
- The custom
hook
Is essentially afunctionIn order touse
At the beginning. - The interior can be used normally
useState
γuseEffect
Or otherHooks
οΌ - Custom return result, format is unlimited;
Here we recommend two third-party custom Hook libraries:
- The react – hooks: nikgraf. Making. IO/react – hooks…
- hooks: github.com/umijs/hooks
5. Two important rules of Hooks ποΈ
(1) Hooks use specification
-
Hooks only work with react function components and custom Hooks, not elsewhere. For example, class components and ordinary functions;
-
Can only be used for top-level code, cannot use Hooks in loops, judgments;
-
React-hooks: react-hooks: react-hooks: react-hooks: react-hooks: react-hooks: react-hooks: react-hooks: react-hooks: react-hooks: react-hooks The configuration is as follows:
// ESLint configuration files { "plugins": [ / /... N lines omitted here... "react-hooks"]."rules": { / /... N lines omitted here... "react-hooks/rules-of-hooks": "error".// Check the Hook rules "react-hooks/exhaustive-deps": "warn" // Check for effect dependencies}}Copy the code
(2) The sequence of calls must be consistent
In react-hooks, call order is a key issue. Why is that?
Let’s use a piece of code to demonstrate:
import React, { useState, useEffect } from 'react'
function Teach({ couseName }) {
// Function components, pure functions, are destroyed upon execution
// So, whether the component is initialized or re-render
// this function is re-executed to get the latest component
// This is different from class components
// render: initializes state value 'zhang SAN'
// re-render: render state
const [studentName, setStudentName] = useState('Joe')
// if (couseName) {
// const [studentName, setStudentName] = useState(' studentName ')
// }
// render: initializes state 'double'
// re-render: render state
const [teacherName, setTeacherName] = useState(Monday Lab)
// if (couseName) {
// useEffect(() => {
// // Simulate student check-in
// localStorage.setItem('name', studentName)
/ /})
// }
// render: add effect function
// re-render: replace effect function (internal function will also be redefined)
useEffect(() = > {
// Simulate student check-in
localStorage.setItem('name', studentName)
})
// render: add effect function
// re-render: replace effect function (internal function will also be redefined)
useEffect(() = > {
// Start the class
console.log(`${teacherName}Class begins, students${studentName}`)})return <div>TeacherName: {couseName}, teacherName: {studentName}</div>
}
export default Teach
Copy the code
The above code demonstrates a function component. In the case of a function component, it is a pure function that is usually destroyed after execution. Therefore, either component initialization or component re-render will re-execute this function to get the latest component. This is different from class components.
Therefore, if we wrap const [studentName, setStudentName] = useState(‘ studentName ‘) in an if statement, it will be blocked. At the same time, if you follow the following order, it is possible to assign the teacherName value to teacherName. Therefore, in react-hooks, we cannot use react-hooks in loops and judgments, which could easily result in out-of-order calls.
Based on the above analysis, let’s summarize this rule:
- Whether it is
render
orre-render
οΌHooks
The order of calls must be consistent; - if
Hooks
Appear in thecycle,judgeIn,There is no guarantee of a consistent order; Hooks
Heavily dependent on call order! Important!
β¨οΈ react-hooks component logic reuse
1. Logical reuse of class components
Class components come in three forms of logical reuse. Respectively is:
- Mixin
- High order component
HOC
- Render Prop
Here are the disadvantages of each of them.
(1) a Mixin
- The source of the variable scope is unclear
- Attribute name repetition
mixins
Too many introductions can cause order conflicts
(2) High-order component HOC
- Component hierarchies are too nested to render and debug easily
HOC
Will be hijackedprops
Must be strictly regulated.Prone to omissions
(3) Render Prop
- Learning is expensive and difficult to understand
- Only pure functions can be passed, and by default pure functions are limited
Now that you’ve seen the shortcomings of the three class components, let’s look at how to reuse component logic using Hooks.
2. Use Hooks for component logic reuse
The essence of using hooks to make components logically reusable is this: custom hooks. Let’s show this with an example.
(1) Example illustration
First of all, we in the project of SRC | customHooks folder to create a new file, named useMousePosition. Js. The specific code is as follows:
import { useState, useEffect } from 'react'
function useMousePosition() {
const [x, setX] = useState(0)
const [y, setY] = useState(0)
useEffect(() = > {
function mouseMoveHandler(event) {
// event.clientX can get the cursor position
setX(event.clientX)
setY(event.clientY)
}
// Bind events
document.body.addEventListener('mousemove', mouseMoveHandler)
// Unbind events
return () = > document.body.removeEventListener('mousemove', mouseMoveHandler)
}, [])
return [x, y]
}
export default useMousePosition
Copy the code
Go on, we in the project of SRC | components folder, create a new file, named CustomHookUsage. Js. This component is primarily used to use the logic in the useMousePosition above. The specific code is as follows:
import React from 'react'
import useMousePosition from '.. /customHooks/useMousePosition'
function App() {
const [x, y] = useMousePosition()
return <div style={{ height: '500px', backgroundColor: '#ccc' }}>
<p>Mouse position {x} {y}</p>
</div>
}
export default App
Copy the code
Finally, we’ll use it in our project’s app.js. The specific code is as follows:
import React, { useState } from 'react';
import CustomHookUsage from './components/CustomHookUsage'
function App() {
return (
<div>
{<CustomHookUsage/>}
</div>
);
}
export default App;
Copy the code
At this point, let’s take a look at the browser display:
Using hooks for component logic reuse is much simpler and more flexible than using class components. Let’s review the benefits of hooks for component logic reuse.
(2) Benefits
Benefits of reusing component logic:
- In full compliance with
Hooks
Original rules, no other requirements, easy to understand and remember; - The scope of variables is clear;
- No component nesting occurs.
πReact Hooks
1. UseState initializes the value, valid only for the first time
Let’s start with a piece of code that looks like this:
import React, { useState } from 'react'
/ / child component
function Child({ userInfo }) {
// render: initializes state
// re-render: only the initialized state value is restored, no new value is set
// Only setName can be used
const [ name, setName ] = useState(userInfo.name)
return <div>
<p>Child, props name: {userInfo.name}</p>
<p>Child, state name: {name}</p>
</div>
}
function App() {
const [name, setName] = useState('Monday')
const userInfo = { name }
return <div>
<div>
Parent
<button onClick={()= >SetName (' Tuesday ')}>setName</button>
</div>
<Child userInfo={userInfo}/>
</div>
}
export default App
Copy the code
The browser displays the following information:
As you can see, when we click, we only change the value of useinfo.name, which is the value of the first Child. Useinfo. name is assigned to name, so why does the second Child not change?
The reason is that, with useState, we always set an initial value to state, and this initial value, even if the data is updated, is only valid for the first time, and the updated value is not assigned to it. So the value of name in the second Child below is always Monday.
2. UseEffect cannot modify state internally
Let’s simulate a piece of code:
import React, { useState, useEffect } from 'react'
function UseEffectChangeState() {
const [count, setCount] = useState(0)
/ / simulation DidMount
useEffect(() = > {
console.log('useEffect... ', count)
// Scheduled task
const timer = setInterval(() = > {
console.log('setInterval... ', count)
setCount(count + 1)},1000)
// Clear a scheduled task
return () = > clearTimeout(timer)
}, []) // Dependency is []
// dependency [] : re-render does not re-execute effect function
// Without dependencies: re-render re-executes effect
return <div>count: {count}</div>
}
export default UseEffectChangeState
Copy the code
Now let’s take a look at the browser:
As you can see, if we had done what we expected, we would have incrementally printed 1234, but it didn’t. In the end, the timer was always printed with an initial value of 0, that is, the state is not used in useEffect.
Therefore, this point in our daily development needs to pay special attention to, it is possible to write the code while writing the bug appears in this.
Now that the problem has arisen, we need to think about its solution. The transformation is as follows:
import React, { useState, useRef, useEffect } from 'react'
function UseEffectChangeState() {
const [count, setCount] = useState(0)
/ / simulation DidMount
const countRef = useRef(0)
useEffect(() = > {
console.log('useEffect... ', count)
// Scheduled task
const timer = setInterval(() = > {
console.log('setInterval... ', countRef.current)
// setCount(count + 1)
setCount(++countRef.current)
}, 1000)
// Clear a scheduled task
return () = > clearTimeout(timer)
}, []) // Dependency is []
// dependency [] : re-render does not re-execute effect function
// No dependencies: re-render will re-execute effect
return <div>count: {count}</div>
}
export default UseEffectChangeState
Copy the code
The browser displays the following information:
If we look at the code above, we can see that we can change the value of state by placing countRef outside useEffect and using useRef.
3. UseEffect may have an infinite loop
Suppose we want to send a network request using AXIos, and we want to add some configuration to the URL, we might do something like this:
import { useState, useEffect } from 'react'
import axios from 'axios'
// Encapsulates a custom Hook for AXIos to send network requests
function useAxios(url, config = {}) {
const [loading, setLoading] = useState(false)
const [data, setData] = useState()
const [error, setError] = useState()
useEffect(() = > {
// Use AXIos to send network requests
setLoading(true)
axios(url, config) // Send a get request
.then(res= > setData(res))
.catch(err= > setError(err))
.finally(() = > setLoading(false))
}, [url, config])
return [loading, data, error]
}
export default useAxios
Copy the code
This seems reasonable, but when config uses a reference data type, i.e. Config ={} or ConfGi =[], useEffect is used in an infinite loop, sending requests indefinitely. Therefore, when we want to pass values to Axios, we can only pass values of primitive data types. The transformation code is as follows:
import { useState, useEffect } from 'react'
import axios from 'axios'
// Encapsulates a custom Hook for AXIos to send network requests
function useAxios(url) {
const [loading, setLoading] = useState(false
const [data, setData] = useState()
const [error, setError] = useState()
useEffect(() = > {
// Use AXIos to send network requests
setLoading(true)
axios.get(url) // Send a get request
.then(res= > setData(res))
.catch(err= > setError(err))
.finally(() = > setLoading(false))
}, [url])
return [loading, data, error]
}
export default useAxios
Copy the code
In this way, data requests to the interface are not sent in an endless loop.
V. π Conclusion
In this article, we first looked at the basic concepts of hooks, then we focused on state and Effect hooks, and we briefly looked at several other hooks. Finally, we talked about the react-hooks contribution to component logic reuse and some things to be aware of.
That concludes my introduction to react-hooks! React-hooks: React-hooks: react-hooks: react-hooks
π£ Egg One More Thing
Previous recommendation
React column here juejin.cn/column/7018…
(: ding
- Pay attention to the public account Monday laboratory, the first time to pay attention to quality articles, more “offer to come” interview column for you to unlock ~
- If you think this article is helpful to you, remember to leave a footprint jio then go yo ~~π
- That’s all for this article! See you next time! π π π