一、每次渲染都是独立的闭包
每次渲染都有它自己的props、state和事件处理函数。当点击事件更新 state 时,函数组件会重新被调用,因为每次渲染都是独立的,所以取到的值不会受后面的操作影响。
import React, { useState } from 'react'
function Example1(){
const [number, setNumber] = useState(0)
const alertNumber = () => {
setTimeout(()=>{
alert(number)
},3000)
}
return (
<div>
<p>{number}</p>
<button onClick={() => setNumber(number+1)}>+</button>
<button onClick={alertNumber}>alertNumber</button>
</div>
)
}
export default Example1
二、函数式更新
如果新的 state 需要通过使用先前的 state 计算得出,那么可以将回调函数当作参数传递给 setState。该回调函数将接收先前的 state,并返回一个更新后的值。
import React, { useState } from 'react'
function Example2(){
const [number, setNumber] = useState(0)
const lazy1 = () => {
setTimeout(() => {
// 获取点击按钮时的 state
setNumber(number+1)
}, 3000)
}
const lazy2 = () => {
setTimeout(() => {
// 每次执行时都会再去获取新的 state,而不是使用点击触发时的 state
setNumber(number=> number+1)
}, 3000)
}
return (
<div>
<p>{number}</p>
<button onClick={() => setNumber(number+1)}>+</button><br />
<button onClick={lazy1}>lazy1:只能获取点击按钮时候的状态</button><br />
<button onClick={lazy2}>lazy2:每次执行都会重新获取state, 所以获取的都是最新的state</button>
</div>
)
}
export default Example2
三、惰性 initialState
initialState 参数只会在组件的初始化渲染中起作用,后续重新渲染时会被忽略。如果 initialState 需要通过复杂计算获得,则需要传入一个函数,在函数中计算并返回 initialState,此函数只会在初始渲染时被调用一次。
import React, { useState } from 'react'
function Example3(props){
console.log('Counter render')
// getInitState 函数只会在初始渲染时执行一次,后续更新状态重新渲染组件时,不会再被调用
const getInitState = () => { number: props.number }
const [counter, setCounter] = useState(getInitState)
return (
<div>
<p>{counter.number}</p>
<button onClick={() => setCounter({ number: counter.number + 1 })}>+</button>
<button onClick={() => setCounter(counter)}>setCounter</button>
</div>
)
}
export default Example3
四、替换非合并
Hook 内部使用 Object.js 来比较新/旧 state 是否相等。
- 与 class 组件中的 setState 方法不同,useState 传入的状态值没有变化,则不重新渲染。
- 与 class 组件中的 setState 方法不同,useState 不会自动合并更新对象。
可以用函数式的 setState 结合展开运算符来达到合并更新对象的效果
import React, { useState } from 'react'
function Example4(){
const [counter, setCounter] = useState({ name:'计数器', number:0 })
console.log('render Counter')
return(
<div>
<p>{counter.name}:{counter.number}</p>
<button onClick={() => setCounter({ ...counter, number: counter.number + 1 })}>+</button>
<button onClick={() => setCounter(counter)}>++</button>
</div>
)
}
export default Example4
总结
- 通过在函数组件中调用 useState 来给函数组件添加内部state。
- useState 会返回一对值:当前状态 和 更新状态的函数。
- react 会在重复渲染时保留这个 state。
- 当更新状态函数被调用后,函数组件会被重新渲染并更新最新的当前 state。
- 更新状态函数是更新 state 变量的方式是替换而不是像 class 的合并。
- useState 唯一的参数就是 initialState,initialState 参数只有在第一次渲染时会被使用。
- 一般来说,在函数退出后变量就会“消失”,内存空间会被释放,而 state 中的变量会被 react 保留。
- 函数内部不存在this调用。