前言
在最初学习react的时候,我们大部分会选择去扒一扒React的官方文档,看看他是什么,怎么使用的。而我却很好奇在文档里学习的第一个完整的组件是 类(Class)组件,但是在实际工作中我们看到项目中所声明的组件都是以函数的形式所展示的。于是一个小小的疑问引起了我对新知识的探索。原来随着项目的发展,组件会被越来越多复杂的状态逻辑和副作用充斥,对于例如事件的监听移除这种成对出现的逻辑被拆分到不同的方法中,而又有很多完全不相关的代码却组合在了一起,就会产出许多的bug。甚至在项目打包Class不能很好的被压缩,并会使热重载出现不稳定的情况等等…Facebook的工程师们想到一个很好的替代方案引入一个API,让函数组件拥有Class组件一样的状态管理的能力,和完整的生命周期函数。他就是Hook。但是我们依旧可以继续使用Class组件。
Hook是什么
Hook是React16.8新增的特性,目的就是为了让开发者在不编写Class的情况下使用state,实现组件的生命周期,Ref以及其他的React特性。
举个例子:
hooks原理
推荐一个博主编写的极简实现useState的博客:React技术揭秘 里边讲述了react useState底层实现的原理。然后配合 GitHub - acdlite/react: A declarative, efficient, and flexible JavaScript library for building user interfaces. React源码尝试着理解Hook理念。
hooks的使用
- 使用StateHook
例如一般我们定义一个state变量
const [count , setCount] = useState(initValue);
initValue:就是设定初始的值
setCount的使用有两种情况
setCount( newValue ); //参数为非函数值时,直接指定新的状态值
setCount( oldValue =>{ … return newValue} ) //参数为函数时,接收原本的状态值,通过函数返回新的状态值
- 使用ReducerHook
一个升级版的useState,在某些场景下useRucer会比useSate更适用,例如state逻辑比较复杂且包含多个子值,或者下一个state依赖之前的state等。 原理类似redux,需要reducer 、action、dispatherinitValue:就是设定初始的值
- 使用EffectHook
可以把useEffect 看做如下三个函数的组合
componentDidMount( );componentDidUpdate( );componentWillUnmount( );useEffect( ( )=>{ }) ; //监听所有useState变量的变化useEffect( ( )=>{return ( ) => { //相当于componentWillUnmount }}) ;useEffect( ()=>{} , [ ] ) ; //不监听任何变量 且函数只会在第一次 render( ) 后执行 useEffect( ( )=>{ } , [name,list]) ; //监听name list变量的变化
- 使用RefHook
const inputEL = useRef( ); 声明一个标记容器<input type="text" ref={ inputEL } />inputEL.current.focus( );inputEL.current.blur( );
- 使用CallbackHook
使用useCallback对函数进行缓存,组件再次更新时(函数组件重新render)时,会根据传入的依赖是否变化决定选用缓存函数还是新函数,比较的方式是浅比较 object.is( ).
我们用useCallback把函数包起来之后,在父组件中只有当Text, isKey变化的时候,才会创建新的handleIneerHTML实例,子组件才会跟着reRender
- 使用MemoHook
要注意区分 useMemo 和 React.memo( ) 的区别哦
如果useMemo(fn, arr)第二个参数匹配,并且其值发生改变,才会多次执行执行,否则只执行一次,如果为空数组[],fn只执行一次
useMemo和useCallback都是reactHook提供的两个API,用于缓存数据,优化性能;两者接收的参数都是一样的,第一个参数表示一个回调函数,第二个表示依赖的数据。
共同的作用:在依赖数据发生变化的时候,才会调用传进去的回调函数去重新计算结果,起到一个缓存的作用
两者的区别:useMemo 缓存的结果是回调函数中return回来的值,主要用于缓存计算结果的值,应用场景如需要计算的状态
useCallback 缓存的结果是函数,主要用于缓存函数,应用场景如需要缓存的函数,因为函数式组件每次任何一个state发生变化,会触发整个组件更新,一些函数是没有必要更新的,此时就应该缓存起来,提高性能,减少对资源的浪费;另外还需要注意的是,useCallback应该和React.memo配套使用,缺了一个都可能导致性能不升反而下降。
- 使用ContextHook
我们想实现一个按钮改变其他组件文字颜色的需求使用context进行数据通信,需要让context组件对所有需要用到共享数据的组件进行包裹,这里的props.childRender( ) 用到的是一个类似插槽的语法,这个由外层父组件传进来的是一个jsx语法,里面是需要共享数据的子组件一下就是子组件通过useContext 拿到的共享数据
TextComponent组件
Buttons组件
- 自定义Hook
一个程序会有多个组件,在其中可能会调用同一个功能,我们为了避免每次需要调用这个功能时就在自己的组件里重复写同一个逻辑,所以我们需要将这个逻辑抽离出来,作为一个公共的Hook 来调用,例如项目中用到的监听窗口尺寸改变的自定义Hook