Open jsonz1993 opened 5 years ago
这篇讲一下目前接触最多的Hook: useEffect,期间也会穿插一些用到的Hooks。
useEffect
一般我们会用它来请求数据或者模拟生命周期里面的一些操作,对于模拟生命周期的操作,我们之前有讲过,可以参考第二篇
这里我我们主要以CNode的接口为例子写个demo,获取某个主题下面的帖子列表并展示出来
常规操作,在CDM里面调用接口,然后setState渲染出来,最简单的demo就写好了
live demo1 下拉框:基础列表
这里扯个点,useEffect 不可以是async的形式,即useEffect(async () => {})。 因为useEffect会将返回的函数作为清除时调用,而async默认就会返回一个Promise,这明显不是我们想要的结果,所以如果这么写React会给你一个警告。
useEffect(async () => {})
但是一般我们的业务都不是这么简单,最起码有多个主题,你好歹得给别人切换一下主题吧?
如果是Class,我们一般会有一个下拉框,然后用户选择的时候setState(type)去改变用户当前选择的主题,再在CDU(ComponentDidUpdate)里面判断this.state.type !== prevState.type去调接口更新数据
setState(type)
CDU(ComponentDidUpdate)
this.state.type !== prevState.type
如果按着这样翻译到Hooks的话,就是useEffect(() => 调接口, [type]),但是我不建议大家还是以Class的思维来写Hooks,这样每次写一个功能首先想到的是Class怎么实现,然后再想着怎么翻译成Hooks,吃力不讨好。
useEffect(() => 调接口, [type])
我们写Hooks或者说成FunctionC的时候,不要一直想着this.state或者DidUpdate之类的。而是类似Observable这种思想: 监听type,如果type变化,那我需要重新去获取接口请求。
type
这里在useEffect的函数体依赖到了外部state:type,所以第二个参数加入我们函数里依赖到的type。
state:type
live demo2 下拉框:提供类型选择
这里我们扩展一下Effect第二个参数依赖的问题。 写useEffect有一个规则 不要欺骗effect,除非特殊情况,否则用到什么依赖就写什么 不然很容易出bug。 那么FunC有一种场景就是,如果我们依赖列表里面有函数怎么处理?
因为如果是组件内部写的函数的话,每次render其实都会重新生成一个函数,这就意味着你如果依赖他,那每次render都会跑effect。
用Hooks的话,我们可以借助 useCallback、useMemo、memo处理这种问题。 memo相当于是PureComponent这里不多赘述。
memo
PureComponent
useMemo其实就是提供一个memoized功能的Hook,使用也简单。 useCallback就是返回值为函数版本的useMemo
useMemo
useCallback
live demo3
回到我们的需求来,我们这里已经把对应的接口获取封装好了,业务上一般都会对接口做基本的报错处理和loading处理。
错误处理还好,现在我们大多用 try...catch 直接在 catch 捕获一个error信息
try...catch
catch
const [errorMsg, setError] = useState('') try { ... } catch(e) { setError(e.message) }
同理loading也直接用一个 state 去存起来,不过我们应该把他放在finally里
finally
const [loading, updateLoading] = useState(false) try { setLoading(true) async... } finally { setLoading(false) }
demo live4 下拉框: loading与错误处理
从上面的图我们可以明显看出,代码里面有很多的useState,过于分散,如果有一些state有依赖的话就更不好处理。 而且我们的接口函数也写在Hook里面,这样很难复用到其他的接口请求,还有就是异步请求很容易有竟态问题。
useState
首先,我们把state都抽离出来,用useReducer来管理(类redux)
useReducer
然后,把获取数据的函数也抽离出来,Hooks内部用一个useRef去保存最新的调用方法
useRef
最后把对应的state返回回来,用到该hooks的组件可以直接拿来就用不用关心内部是怎么维护这一份state的,而且 这种提供局部state抽离的模式,用ClassC我没想到怎么实现,这也是FunC最吸引我的一点。
const [posts, postsLoading, postsError] = useService(type, serverGetPosts)
demo live5 下拉框: 独立封装
到这里我们已经封装好一个包含loading、错误处理的接口获取Hooks,当然里面还有很多可以根据自己需要改动的。
这只是一个用来了解Hooks的一个例子,业务上我们可能更多的是使用redux/rematch,loading也会有对应的plugin直接拿来就用。
redux/rematch
到这里关于useEffect使用及一些常见问题讲得七七八八了
最后讲一下目前项目上实践中遇到的一些小问题,一开始用Hooks的时候并没有了解太多,纯粹觉得这个东西比较好玩又是趋势,刚好碰到要做个新项目就在部分页面用FunC+Hook代替ClassC的写法。
所以多多少少会吃文化上的亏,不过如果这几篇看下来应该小问题都还好,至少概念和用法场景都会比较清晰。
场景是这样的,有一个表格的 columns 配置,里面有一些是涉及到组件dispatch等的操作,所以你没办法把它抽离到最外层,只能丢在组件内。
dispatch
但是我又有点强迫症,不想每一次渲染都去重新赋值(里面涉及到一些计算),想着平时这种情况,我都是写在 CDM 里面,于是就有下面的代码
这里为什么不生效呢? 大家心里应该都有答案,也正是碰到这种"奇奇怪怪"的问题之后,我才会想到需要系统一点去了解Hooks的东西,也才有这个系列的水文
这个问题困扰我挺久的,不过不关Hooks事可能说成是FunC的问题会贴切一点。 写ClassC的时候,你可以很好的把方法逻辑拆分为Class的方法属性,清晰明了很多。 约定方法的顺序,以及命名之后,感觉要找逻辑或者划分功能非常容易
但是到了Func就会总觉得一个函数体太大太长,而且个人不大喜欢函数里面嵌一堆的函数,然后共用一份变量,类似这样 条理没有ClassC那么好,而且其实Hooks抽离有一定的难度,你要对业务足够熟悉,知道哪一些有可能公用才能抽出来。
绝大多数情况,前端开发的时候,可能连文档都还没给全甚至设计图都没给就开撸,你都不知道后面会有什么其他的逻辑可以公用,一不小心又可能变成过度设计(过早优化)。
不过还是很看好Hooks,毕竟这东西就是想推我们从ClassC走向FunC,这与React的编程模型更加贴切 React is a UI Runtime: data => UI
React is a UI Runtime: data => UI
以上说到的很多关于Hooks的问题,比如依赖缺失、在useEffect初始化外部变量、没有写在最顶层等问题,都可以通过eslint-plugin-react-hooks 去发现,所以尽早给你的项目加上吧,谁用谁知道。
后面可能会继续在项目里面尝试FunC+Hooks的实践,也可能会等社区尝试出最佳实践再继续写。
最后附上学习过程看到的不错的网站 How to fetch data with React Hooks? A Complete Guide to useEffect
水完告辞
谢谢 分享收获很大
这篇讲一下目前接触最多的Hook:
useEffect
,期间也会穿插一些用到的Hooks。一般我们会用它来请求数据或者模拟生命周期里面的一些操作,对于模拟生命周期的操作,我们之前有讲过,可以参考第二篇
这里我我们主要以CNode的接口为例子写个demo,获取某个主题下面的帖子列表并展示出来
基本的Demo
常规操作,在CDM里面调用接口,然后setState渲染出来,最简单的demo就写好了
live demo1 下拉框:基础列表
这里扯个点,useEffect 不可以是async的形式,即
useEffect(async () => {})
。 因为useEffect会将返回的函数作为清除时调用,而async默认就会返回一个Promise,这明显不是我们想要的结果,所以如果这么写React会给你一个警告。参数变换
但是一般我们的业务都不是这么简单,最起码有多个主题,你好歹得给别人切换一下主题吧?
如果是Class,我们一般会有一个下拉框,然后用户选择的时候
setState(type)
去改变用户当前选择的主题,再在CDU(ComponentDidUpdate)
里面判断this.state.type !== prevState.type
去调接口更新数据如果按着这样翻译到Hooks的话,就是
useEffect(() => 调接口, [type])
,但是我不建议大家还是以Class的思维来写Hooks,这样每次写一个功能首先想到的是Class怎么实现,然后再想着怎么翻译成Hooks,吃力不讨好。我们写Hooks或者说成FunctionC的时候,不要一直想着this.state或者DidUpdate之类的。而是类似Observable这种思想: 监听
type
,如果type变化,那我需要重新去获取接口请求。这里在useEffect的函数体依赖到了外部
state:type
,所以第二个参数加入我们函数里依赖到的type。live demo2 下拉框:提供类型选择
useEffect依赖问题
这里我们扩展一下Effect第二个参数依赖的问题。 写useEffect有一个规则 不要欺骗effect,除非特殊情况,否则用到什么依赖就写什么 不然很容易出bug。 那么FunC有一种场景就是,如果我们依赖列表里面有函数怎么处理?
因为如果是组件内部写的函数的话,每次render其实都会重新生成一个函数,这就意味着你如果依赖他,那每次render都会跑effect。
用Hooks的话,我们可以借助 useCallback、useMemo、memo处理这种问题。
memo
相当于是PureComponent
这里不多赘述。useMemo,useCallback
useMemo
其实就是提供一个memoized功能的Hook,使用也简单。useCallback
就是返回值为函数版本的useMemo
live demo3
loading与报错等通用接口调用封装
回到我们的需求来,我们这里已经把对应的接口获取封装好了,业务上一般都会对接口做基本的报错处理和loading处理。
错误处理还好,现在我们大多用
try...catch
直接在catch
捕获一个error信息同理loading也直接用一个 state 去存起来,不过我们应该把他放在
finally
里demo live4 下拉框: loading与错误处理
custom Hooks
从上面的图我们可以明显看出,代码里面有很多的
useState
,过于分散,如果有一些state有依赖的话就更不好处理。 而且我们的接口函数也写在Hook里面,这样很难复用到其他的接口请求,还有就是异步请求很容易有竟态问题。首先,我们把state都抽离出来,用
useReducer
来管理(类redux)然后,把获取数据的函数也抽离出来,Hooks内部用一个
useRef
去保存最新的调用方法最后把对应的state返回回来,用到该hooks的组件可以直接拿来就用不用关心内部是怎么维护这一份state的,而且 这种提供局部state抽离的模式,用ClassC我没想到怎么实现,这也是FunC最吸引我的一点。
demo live5 下拉框: 独立封装
到这里我们已经封装好一个包含loading、错误处理的接口获取Hooks,当然里面还有很多可以根据自己需要改动的。
这只是一个用来了解Hooks的一个例子,业务上我们可能更多的是使用
redux/rematch
,loading也会有对应的plugin直接拿来就用。到这里关于
useEffect
使用及一些常见问题讲得七七八八了最后讲一下目前项目上实践中遇到的一些小问题,一开始用Hooks的时候并没有了解太多,纯粹觉得这个东西比较好玩又是趋势,刚好碰到要做个新项目就在部分页面用FunC+Hook代替ClassC的写法。
所以多多少少会吃文化上的亏,不过如果这几篇看下来应该小问题都还好,至少概念和用法场景都会比较清晰。
问题:用useEffect初始化某些配置
场景是这样的,有一个表格的 columns 配置,里面有一些是涉及到组件
dispatch
等的操作,所以你没办法把它抽离到最外层,只能丢在组件内。但是我又有点强迫症,不想每一次渲染都去重新赋值(里面涉及到一些计算),想着平时这种情况,我都是写在 CDM 里面,于是就有下面的代码
这里为什么不生效呢? 大家心里应该都有答案,也正是碰到这种"奇奇怪怪"的问题之后,我才会想到需要系统一点去了解Hooks的东西,也才有这个系列的水文
问题:Hooks最佳实现与代码过长问题
这个问题困扰我挺久的,不过不关Hooks事可能说成是FunC的问题会贴切一点。 写ClassC的时候,你可以很好的把方法逻辑拆分为Class的方法属性,清晰明了很多。 约定方法的顺序,以及命名之后,感觉要找逻辑或者划分功能非常容易
但是到了Func就会总觉得一个函数体太大太长,而且个人不大喜欢函数里面嵌一堆的函数,然后共用一份变量,类似这样 条理没有ClassC那么好,而且其实Hooks抽离有一定的难度,你要对业务足够熟悉,知道哪一些有可能公用才能抽出来。
绝大多数情况,前端开发的时候,可能连文档都还没给全甚至设计图都没给就开撸,你都不知道后面会有什么其他的逻辑可以公用,一不小心又可能变成过度设计(过早优化)。
不过还是很看好Hooks,毕竟这东西就是想推我们从ClassC走向FunC,这与React的编程模型更加贴切
React is a UI Runtime: data => UI
esling-react-hook
以上说到的很多关于Hooks的问题,比如依赖缺失、在useEffect初始化外部变量、没有写在最顶层等问题,都可以通过eslint-plugin-react-hooks 去发现,所以尽早给你的项目加上吧,谁用谁知道。
后面可能会继续在项目里面尝试FunC+Hooks的实践,也可能会等社区尝试出最佳实践再继续写。
最后附上学习过程看到的不错的网站 How to fetch data with React Hooks? A Complete Guide to useEffect
水完告辞