写点什么

React Hooks - 如何安全地使用 state

用户头像
Daniel
关注
发布于: 2021 年 06 月 03 日
React Hooks - 如何安全地使用state

Hi,丹尼尔,今天我们来聊一聊 "如何安全地使用 state" 怎么样?


丹尼尔:安全?难道使用 React Hooks 的 state 还会带来危险?X﹏X 紧张~


放松放松,关于安全,等会我给个例子就会很好理解了。没有例子的辅助,好难一下子说明白,也可能是我的表达能力 So So 吧!


丹尼尔:好吧。在场的各位同学,有不清楚 React Hooks 是什么的吗?


也许有一些同学还不太了解,我就简单地介绍下(突然有种凑字数的嫌疑 ヘ(・_|):


React Hooks 是个好东西,它让你可以用纯函数来实现有状态的组件,从此你不用再纠结组件到底是有状态还是无状态,从而在纯函数和类两种实现间徘徊(当然好处多多,绝不止这个,后面我会专门写一篇介绍 Why React Hooks 的文章滴😬)。


一旦你踏上 React Hooks 的征途,就会碰到各种各样奇怪的问题。


这很正常,一个新事物的诞生,总是伴随着各种问题,然后在打怪中不断升级成长,最终成为。。。


丹尼尔:喂喂,别扯远了


🤪如果你有看过关于 React Hooks 的官方文档,看过里面的一些例子,你可能会觉得,挺简单的,不过是改成用 useState 来使用 state 而已,没啥难度。


然而就在你放松警惕的这个 moment,“危险”正在悄悄地在某些角落降临了。


丹尼尔:紧张,什么危险,不会失身吧


😅 额~~


对于 state 值的获取,你可能取到了一个不是你所期望的旧值,即不是最新的状态值。


你需要时时刻刻保持清醒,才有可能绕过这些“危险”。


丹尼尔:😵


看着你一脸蒙 B 的样子,我这就赶紧给个例子哈。


栗子来了:当你在表单上填写了一些信息,然后离开表单时,希望能自动保存为草稿,以便下次进来时能还原,省去重新填写的麻烦。


下面给出简化版的实现:


import React, { useState, useEffect } from "react";
export const UseStateDemoComp1 = () => { const [name, setName] = useState('daniel');
useEffect(function() { return () => { saveDraft() } }, [])
function saveDraft() { console.log('Save Draft:', name); }
return ( <div> <form> <input value={name} onChange={e => setName(e.target.value)}></input> </form> </div> );};
复制代码


昨一看,代码好像没啥问题。


传给 useEffect 的 function 返回了一个 function,该 function 相当于原来的componentWillUnmount生命周期方法,即只在组件快销毁的时候才会调用执行。在该方法里面执行一些逻辑,这里调用saveDraft方法,获取 name state 的值,然后保存。


找出问题了吗?如果没有,那我们直接看个动图吧,看看问题所在:



点击 Toggle 按钮会销毁掉组件,所以会触发销毁动作。从动图可以看到,我们填写的是“sarah”,但在销毁执行的方法中,取到的值却是“daniel”


丹尼尔:这是为什么呢?不应该是最新的值吗?😶


因为useEffect的依赖是个空数组,所以在整个组件生命周期只会执行一次,即在组件第一次完成渲染后执行,而useEffect的方法里面用到的 state 值就是当时最新的 state 值,可以用快照来理解。在接下来的时间里,无论组件重新渲染多少次,都不会改变里面的 state 的值,因为它只是当时的快照


有同学可能会说,把 name 加进 useEffect 依赖的数组里面就行了。如下:


useEffect(function() {    return () => {      saveDraft()    }}, [name])
复制代码


看上去好像是满足了需求,但依然有问题。因为我只想在组件退出的时候才保存,但现在的效果就是表单字段值一旦改变,就会保存,保存操作变得非常的频繁。



丹尼尔:所以你想说 React Hooks 对此毫无办法?


当然不是啦,通过 useRefuseEffect 是可以实现上面的需求的。怎么实现?同学们就自己动下脑筋吧。当你实现出来后,你会发现代码不仅啰嗦,可读性还差。


丹尼尔:好了,请开始你的表演吧


😎有了上面的例子,这里就可以对安全地使用state作个描述了:


“安全地使用 state,就是无论你在何时何地以何种方式读取 state 的值时,它总是符合你的预期,总是最新的那个值,而不用你小心翼翼地去判断它会不会是个没更新的旧值”


官方提供了自定义 Hooks 的能力,就是想通过社区的共同努力,来持续完善 Hooks。


接下来我们通过 【nice-hooks】这个第三方自定义 Hooks 的开源项目,使用它的 useSingleState 来代替 useState,话不多说,直接上代码


import React, { useEffect } from "react";import { useSingleState } from "nice-hooks";
export const DemoComp1 = () => { const [state, setState] = useSingleState({ name: 'daniel' });
useEffect(function() { return () => { saveDraft() } }, [])
function saveDraft() { console.log('Save Draft:', state.name); }
return ( <div> <form> <input value={state.name} onChange={e => setState({name: e.target.value})}></input> </form> </div> );};
复制代码


先来个动图直接看下效果。完美~



这里介绍一下useSingleState这个 hook:它使用类似于 class 形式的 this.statethis.setState 的方式来使用 state,所以非常容易上手。最重要是它可以安全地使用 state,并且拥有 callback 能力。


最后我们使用 useLifeCycle 这个 hook 对代码进一步完善。好了,下面的代码成品相比于直接使用官方的 hooks,是不是感觉好多了。


import React from "react";import { useSingleState, useLifeCycle } from "nice-hooks";
export const DemoComp1 = () => { const [state, setState] = useSingleState({ name: 'daniel' });
useLifeCycle({ willUnmount() { saveDraft() } })
function saveDraft() { console.log('Save Draft:', state.name); }
return ( <div> <form> <input value={state.name} onChange={e => setState({name: e.target.value})}></input> </form> </div> );};
复制代码


时间真快,又到了该说再见的时候。


如果你觉得这篇文章写得还可以,还请点个赞哦。


如果你觉得 nice-hooks 还不错,还请加颗☆哦。


ByeBye!




Keywords: react, hooks, nice-hooks

发布于: 2021 年 06 月 03 日阅读数: 17
用户头像

Daniel

关注

一源一世界 2019.03.03 加入

ncform / ncgen / nice-hooks 开源项目作者

评论

发布
暂无评论
React Hooks - 如何安全地使用state