1. Basic use
useState
It is a hook function provided by react to define responsive variables. The basic syntax is as follows:
const [count, setCount] = useState(initialCount)
- It returns a state and a method to modify the state, and the state needs to be modified through this method;
initialCount
It is an initial state we pass in, it is lazy, we can pass a function to return a value as the initial state, and this function will only be executed once during the initial rendering;
const [count, setCount] = useState(() => {
const initialCount = someExpensiveComputation();
return initialCount
})
Next, apply the defined state to the page:
import { useState } from 'react'
function App() {
const [count, setCount] = useState(0)
const handleClick = () => {
setCount(count + 1)
// 传入一个函数,更新的值是基于之前的值来执行
// setCount(count => count + 1)
}
return (
<div>
<h4>count: {count}</h4>
<button onClick={ handleClick }>点击更新状态</button>
</div>
)
}
After the page is rendered, we can see count
that the value of is 0. When we click the button, count
the value of will be increased by 1, and the page will be updated at the same time;
After understanding the basic usage, we can think about several questions;
setCount
Is it synchronous or asynchronous when modifying the value?setCount
What happens with consecutive calls ?
First question: setCount
is it synchronous or asynchronous when modifying the value?
const handleClick = () => {
console.log("value1: ", count)
setCount(count => count + 1)
console.log("value2: ", count)
}
From the figure, we can see that the value of the page is updated, but the console prints the previous value. Does this also mean that setCount
it is asynchronous? Let's change the method and use asynchronous to modify the state;
const handleClick = () => {
console.log("value1: ", count)
setTimeout(() => {
setCount(count => count + 1)
console.log("value2: ", count)
}, 0)
}
Obviously, the result of asynchronously modifying the state is the same as synchronously modifying the state, which also shows setCount
that is updated asynchronously; then how do we get the updated value? We can use another hook function useRef
, the code is as follows:
function App() {
const [count, setCount] = useState(0)
const countRef = useRef(count)
countRef.current = count
const handleClick = () => {
setCount(count => count + 1)
console.log("value3: ", count)
setTimeout(() => {
console.log(countRef.current)
}, 0)
}
return (
<div>
<h4>count: {count}</h4>
<button onClick={handleClick}>点击更新状态</button>
</div>
)
}
From the figure, we can see that we have obtained the updated value, which useRef
can not only be used to access DOM nodes, but also can be used to represent a container. current
The property can hold any value, and useRef
the returned object will be maintained throughout the life cycle. ;
Second question: what setCount
happens ?
(1) Pass in a state-based value
const handleClick = () => {
console.log("value1: ", count)
setCount(count + 1)
console.log("value2: ", count)
setCount(count + 1)
console.log("value3: ", count)
}
As can be seen from the picture, if we pass in an ordinary value, it will only be updated for the last time;
(2) Pass in a function
const handleClick = () => {
console.log("value1: ", count)
setCount(count => count + 1)
console.log("value2: ", count)
setCount(count => count + 1)
console.log("value3: ", count)
}
It can be seen that if a function is passed in, it will perform two assignments, because the updated value is executed based on the previous value, so it is recommended to modify it in the form of function passing in during development;
2. Matters needing attention
1. Modification of complex variables
For variables of complex types, we need to redefine when we modify them. Modifications based on the original data will not cause re-rendering of components, because the update mechanism of React components only performs shallow comparisons, that is, when updating a complex type of data, only its If the reference address does not change, the component will not be re-rendered; for example
function App() {
const [arr, setArr] = useState([1])
const pushData = () => {
arr.push(4)
setArr(arr)
}
return (
<div>
<h4>{arr.join("-")}</h4>
<button onClick={pushData}>点击添加数组</button>
</div>
)
}
When the above code clicks the button, the view will not change, but the value arr
of change. If you want to modify this array, you need to redefine an array to modify it. The modification on the original array will not cause the component to re-render. React components The update mechanism only performs a shallow comparison, that is, when updating a complex type of data, as long as its reference address does not change, the component will not be re-rendered;
const pushData = () => {
setArr([...arr, 4])
}
2. Asynchronous operation to get updated value
In a class component, the updated value can be obtained by asynchronous operation when modifying the value, but in the function component, the updated value cannot be obtained by asynchronous acquisition. For example, compare:
class component
class App extends React.Component {
constructor() {
super()
this.state = {
count: 0
}
}
handleClick = () => {
this.setState({
count: this.state.count + 1
})
console.log(this.state.count)
setTimeout(() => {
console.log(this.state.count)
})
}
render() {
return (
<>
<h4>count: {this.state.count}</h4>
<button onClick={this.handleClick}>点击更新状态</button>
</>
);
}
}
function component
function App() {
const [count, setCount] = useState(0)
const handleClick = () => {
setCount(count => count + 1)
console.log("value1: ", count)
setTimeout(() => {
console.log("value2: ", count)
})
}
return (
<div>
<h4>count: {count}</h4>
<button onClick={handleClick}>点击更新状态</button>
</div>
)
}
Obviously, the updated value cannot be obtained asynchronously in a function component, we can obtain useRef
it through ;
const countRef = useRef(count)
countRef.current = count
const handleClick = () => {
setCount(count => count + 1)
console.log("value1: ", countRef.current)
setTimeout(() => {
console.log("value2: ", countRef.current)
})
}