令人困扰的问题
是因为异步吗
大家都知道,react的useState和useReducer,在set和dispatch之后,马上去获取值,是拿不到最新的值的。这个原因之一是useState和useReducer是异步更新的,也就是说我们刚调用完set和dispatch马上去取值,这种同步代码执行的时候,状态还没更新呢,所以拿不到。
同步如何
那我们如果换成redux呢?redux不使用中间件扩展的话,全部都是同步的操作,也就是说你上面一行dispatch之后,马上状态的更新就完成了。但是其实我们马上去获取,也是拿不到最新的状态的。其根本原因是React的机制。
React的理念和机制
React的快照理念,导致了上述的问题。我们就拿React的函数组件来举例子,我们知道函数组件所谓的渲染就是执行一遍自己,所以组件重新渲染就会重新执行这个函数。那我们定义过的变量也就会被重新定义,也就是说,当你写const [value,setValue] = useState(0)
的时候,每次函数重新渲染,value都被重新定义了一遍,并且被重新赋了值。这就是value虽然是const,但是仿佛能被改变值的原因。
React的理念如此,页面是某个状态的快照,状态和页面是一一对应的。状态变了,就要重新渲染。而组件每次重新渲染,都对应着一个新的状态,新的状态和旧的状态是完全隔离的,打个比方就像平行宇宙一样。每次重新渲染,就分出来一个平行宇宙。两个平行宇宙的你,可能相似,但不是一个人。
也就是说,更新后的状态,只存在于新的平行宇宙里。你setValue之后,再去获取value,获取的还是老的value。新的value值已经在下一次执行了,需要下一次渲染才能拿到了,所以这样是必定拿不到更新后的值的。
如何能拿到更新后的值
一种方法是使用useEffect。当每次函数重新渲染后,都会执行useEffect,这个时候你是正处在新一次函数执行中的,所以是能拿到新定义的value值的。但是这样有个问题,就是如果useState的变量是个引用值,然后你要监听的是引用值里面的某个属性,当这个属性变化的时候去更改另一个属性,这样就会导致函数无限重新渲染。比如
1 | const [obj,setObj] = useState{a:0,b:1} |
我想做的是,每当obj.b变化的时候,我要去更新obj.a的值。我们首先不能直接obj.a=xxx这样去做,肯定要setObj(newObj)
这样去更改a的值,这就会导致我们监听的是obj,但是又会改obj的值,这时候这种方法就行不通了。
另一种方法借助useRef。useRef很特殊,返回一个带current属性的对象。每次重新渲染,函数组件都重新执行,但是useRef不会被重新定义,一直是最初的那个变量。因此useRef相当于是可以跨越不同平行宇宙的存在。我们可以将新的变量值赋给useRef,然后就能拿到新的值了。
我们可以封装一个自己的hook:
1 | const useGetState = (initialState) => { |
我们可以通过第三个返回值,来即刻拿到更新后的值。就先说这么多吧,困了,我恨加班