前言:前端小白一枚,入行前端不到一年,热衷于技术,所以写一些文章记录下自己的学习的历程。 对于刚入门React的我,useState无疑是接触最多的一个hooks,下面介绍下我目前学习到的知识
基础介绍
- 它返回一个状态和一个修改状态的方法,状态需要通过这个方法来进行修改;
- initialCount 是我们传入的一个初始状态,它是惰性的,我们可以通过传一个函数来返回一个值当作初始状态,并且这个函数只会在初始渲染时执行一次;
const [state, setState] = useState(initialState);
写法介绍
官网中有介绍到:我们用方括号定义了一个 state 变量。
const [count, setCount] = useState(0);
当我们使用useState定义state 变量时候,它返回一个有两个值的数组。第一个值是当前的 state,第二个值是更新 state 的函数。使用 [0] 和 [1] 来访问有点令人困惑,这是js的数组结构,那为什么不用对象结构呢?
数组和对象解构的区别:
1、数组的元素是依次排序的,数组解构时变量的取值由数组元素决定,变量名称可以任意命名
2、对象的属性没有次序,解构时,变量名称必须和属性相同,才能取到正确的值。
3、因此,数组会更加灵活,可以任意命名和修改state
从上述区别可知:因为对象结构返回的是对象,在解构对象的时候必须要和 useState 内部实现返回的对象同名,想要使用多次的话,必须得设置别名才能使用返回值。如果useState 返回的是数组,那么使用者可以对数组中的元素命名,代码看起来也比较干净。
问题:更新后无法立马获取最新值
当我更新完状态后,我马上打印这个值,发现值并未改变,但是视图却更新了
const DrawCom = ()=>{
const [state,setState] = useState(0)
const btnClick = ()=>{
setState(1)
console.log('state',state)//打印出来的值仍然是0
}
return(
<>
<p>{state}</p>
<Button onClick={btnClick}>Click</Button>
</>
)
}
然后我去查询资料,发现useState的更新是异步的,然后综合了一些文章,简单总结下更新过程:
- useState每次执行会返回一个新的state(简单类型的等值拷贝)
- setState会触发UI更新(重新render,执行函数组件)
- 由于UI更新是异步任务,所以setState也是一个异步过程
所以如果我们需要在更新状态完成的同时并拿取到当前的值,可以这样写
使用useState的返回值
const btnClick = ()=>{
setState(pre=>{
pre+=1
console.log('pre',pre)
return pre
})
}
使用useEffect()函数来辅助
const DrawCom = ()=>{
const [state,setState] = useState(0)
/*
useEffect用于处理组件中的effect,通常用于请求数据,事件处理,订阅等相关操作
简单来说就是可以用来监听状态等,第一个参数是函数,第二参数为可选,
为空的时候每次组件渲染都会执行一次,空数组时只执行一次。如果是变量,那么变量变化就会执行一次
*/
useEffect(()=>{
console.log(state)
},[state])
const btnClick = ()=>{
setState(1)
}
return(
<>
<p>{state}</p>
<Button onClick={btnClick}>Click</Button>
</>
)
}
使用自定义的hooks
这里的方法是某个大佬文章看来的,具体文章链接忘了,大佬的思路是封装为一个类似于Promise的链式函数
const DrawCom = () => {
const useStateWithCall = (initValue) => {
const ref = useRef(0)
const callFRef = useRef()
const setFuncRef = useRef()
let [state, setState] = useState(initValue)
if (!ref.current) {
ref.current = 1;
setFuncRef.current = (newData, callF) => {
callFRef.current = callF
setState(newData)
return Promise.resolve(newData)
}
}
return [state, setFuncRef.current]
}
const [state, setState] = useStateWithCall(0)
const btnClick = () => {
setState(1).then(val => {
console.log('val',val)
})
}
return (
<>
<p>{state}</p>
<Button onClick={btnClick}>Click</Button>
</>
)
}
问题:多次更新只生效一次
const DrawCom = ()=>{
const [state,setState] = useState(0)
const btnClick = ()=>{
setState(state+1)
setState(state+1)
console.log(state)
}
return(
<>
<p>{state}</p>
<Button onClick={btnClick}>Click</Button>
</>
)
}
上述代码执行了两次加1的操作,但是视图还是只有一次加1的显示。造成这个原因是因为两次setState(n+1)时候,实际上形成了两个闭包,都保存了对此时state的状态(state=0)的引用。
在setN后:
- 先分别生成了两个新的n,数值上都等于state+1(即1),但彼此无关。
- 分别进行了render,而只有最新一次render有效,此次render引用了最后一次setN函数里生成的state。
解决办法其实很简单,利用函数,接收旧值,进行更新
const btnClick = ()=>{
setState(state=>state+1)
setState(state=>state+1)
//这里就很进行两次操作,视图也会+2
}
总结:对于useSate的使用以及目前遇到问题就暂时这些,以后如果有其他还会慢慢补充。最后,前端小白加写作小白一枚,刚开始起步,如果哪里写得不对,哪里文笔表达有误,请各位大佬不吝赐教(* ̄︶ ̄)