一 、前言
在16.8版本以前,我们用class编写组件,但是class组件逻辑难以复用,生命周期也比较复杂,hook 是React16.8 的新特性,它可以让你在不编写class的情况下使用state以及其他react特性,在hooks之前函数组件是无状态的,也就是说 函数组件中不能拥有自己的状态(state),也不能拥有监听组件的生命周期,hooks提供了useState来维 护函数组件内部的状态,useEffect聚合了多个生命周期函数,Hook 是一些可以让你在函数组件里“钩入” React state 及生命周期等特性的函数。
首先我们先快速的回顾一下之前学习过的各个hook:
定义变量
useState:定义普通变量
useReducer:定义有不同类型、参数的变量
组件传值
useContext():定义和接收具有全局性质的属性传值对象,必须配合React.createContext()使用
对象引用
useRef:获取渲染后的DOM元素对象,可调用该对象原生html的方法,可能需要配合React.forwardRef()使用
useImperativeHandle:获取和调用渲染后的DOM元素对象拥有的自定义方法,必须配合React.forwardRef()使用
生命周期
useEffect:挂载或渲染完成后、即将被卸载前,调度
useLayoutEffect:浏览器把内容真正渲染到界面之前,同步调度
性能优化
useCallback:获取某处理函数的引用,必须配合React.memo()使用
useMemo:获取某处理函数返回值的副本
代码调试
useDebugValue:对react开发调试工具中的自定义hook,增加额外显示信息
自定义hook
useCustomHook:将hook相关逻辑代码从组件中抽离,提高hook代码可复用性。
在 React 中有两种流行的方式来共享组件之间的状态逻辑: render props 和高阶组件,HOC和render props 方式都会侵入代码,使得代码阅读性下降,也不够优雅,组件内部暴露的value值,在外部也很难拿到,现在让我们来看看 Hook 是如何在让你不增加组件的情况下解决相同问题的。
自定义Hook本质上是一种函数代码逻辑的抽取,类似vue2.0的mixin。 反观 hook 的写法,逻辑完全解耦,使用场景最大化且不侵入代码,在组件顶层可以拿到双向绑定的值,比之前优雅很多。 自定义hook是目前为止,解决逻辑片段复用的最佳方案
下面我们带着疑问开始今天关于React 的自定义hook的探讨
- 什么是自定义hooks?
- 自定义hook是来解决什么问题的?
- 自定义hooks-触发条件?
- 自定义hook基本用法?
- 有了react hook 还需要使用redux吗?
什么是 自定义 hook ?
像useState、useEffect、useContext、useReducer等这10个hook是react默认自带的hook,而所谓自定义hook就是由我们自己编写的hook。 自定义hook 专注的就是逻辑复用,它们不是共享状态的方法,而是共享有状态逻辑的方法, 使我们的项目,不仅仅停留在组件复用的层面上。 提取自定义hook让我们可以将一段通用的逻辑存封起来。将我们需要它的时候,开箱即用即可,函数是对某一个功能的封装,那么 hook 就是对相同逻辑功能的封装。
对数组每一项转大写的逻辑提成如下就是一个简单的自定义hook:
function useFormatList(list) {
return useMemo(() => list.map(item => {
return item.toUpperCase()
}), [])
}
我们在设置自定义hooks的时候,一定要把条件限定-性能开销加进去 ,所以一个好用的自定义hooks,一定要配合useMemo ,useCallback 等api一起使用。
使用:
const list=[ 'aaa' , 'bbb' , 'ccc' ]
const newList = useFormatList(list);
<div className="list" > { newList.map(item => <div key={item}>{ item }</div>) } </div>
1、如果你在别人的项目代码中,发现除了react默认自带的那10个hook以外,出现了 useXxx() 这样的看着像hook的函数,可以肯定那些就是自定义的hook。 2、随着react新版本发布,可能会出现更多新的、默认自带的hook
自定义hook是来解决什么问题的?
当你觉得重复在做一件事情的时候,那么你会把它封装成一个函数。同理,当你觉得在写重复的逻辑时,那么它就可以是一个 hook。 通过自定义 Hook,可以将组件逻辑提取到可重用的函数中
例如数组的forEach, map, filter等方法。在所有的for循环中,共同的逻辑是对每一个元素的遍历,也就是所有的方法可以公用forEach
Array.prototype.forEach = function () {
const ary = this;
const [callbackfn, thisArg] = [].slice.call(arguments);
if (typeof callbackfn !== 'function') {
throw new TypeError(callbackfn + 'is not a function')
}
for (let i = 0; i < ary.length; i++) {
callbackfn.call(thisArg, ary[i], i, ary);
}
}
map 循环过程中使用了forEach,返回新的子项,最终组成新的数组。
function map(arr, fn) {
const res = [];
arr.forEach((item, i) => {
res[i] = fn(item, i);
})
return res;
}
我们可以非常简单的基于forEach实现some, every等方法。react 自定义hook 的逻辑片封装思维与此类似。理解了这个思维,我们便很快能辨别出什么场景下需要使用自定义hook了
实际项目中的使用场景 在文章下方介绍~
自定义hooks-触发条件
hooks本质上是一个函数。函数的执行,决定与无状态组件组件自身的执行上下文。每次函数的执行(本质上就是组件的更新)就会执行自定义hooks的执行,由此可见组件本身执行和hooks的执行如出一辙。 那么prop的修改,useState,useReducer使用是无状态组件更新条件,那么就是驱动hooks执行的条件
自定义hook基本用法?
- 自定义hook其实是一个函数,但是要以use开头,函数内部可以调用 其他hook
- 自定义hook复用的是逻辑,而非状态
- hook只能用在函数组件中或者自定义 hook里
首先我们知道hook只能用在函数组件中,而函数组件本身是一个稍微特殊的函数,尽管稍微特殊但毕竟他也遵循一般函数的使用规律。
所谓“把原来写在函数组件内的hook相关代码抽离出来,单独定义成一个函数” 本质上就是把函数内部定义的变量或方法拿出来,放到函数外面单独定义成一个函数。
这个抽离出来新定义的函数,遵循JS默认的函数用法,即函数参数可以任意设定,返回值也可以是任意内容。
使用 返回的东西是我们真正需要的。更像一个工厂,把原材料加工,最后返回我们,其内部可以使用react自带的useState 等,可能引起render,数据可以达到及时更新效果。
const [ xxx , ... ] = useXXX(参数A,参数B...)
有了react hook 还要 redux 么?
是不是可以轻松的回答他,这两个是不同的东西,hook 是共享状态的逻辑,而 redux 是共享状态和管理状态的。 不过使用 hook 可以很容易封装一个简易的全局共享状态。对于数据流复杂的项目,我依然建议使用 redux 去管理你的数据流。
总结
提取自定义hook 可读性高,也易于维护。 自定义hook 不会侵入代码, 不会造成嵌套。 自定义hook UI和逻辑彻底拆分,更容易复用。