当前位置:首页 > React > 正文

玩转react-hooks,自定义hooks设计模式及其实战

前言

自从react16.8,react-hooks诞生以来,在工作中一直使用hooks,一年多的时间里,接触的react项目,渐渐使用function无状态组件代替了classs声明的有状态组件,期间也总结了一些心得。尤其对于近期三个月的项目里,一点点用自定义hooks来处理公司项目中重复逻辑,总体感觉还不错。今天给大家讲讲我在工作中对react-hooks心得,和一些自定义hooks的设计思想,把在工作中的经验分享给大家。

自定义hooks设计

又回到那个问题?什么是hooks。

react-hooks是react16.8以后,react新增的钩子API,目的是增加代码的可复用性,逻辑性,弥补无状态组件没有生命周期,没有数据管理状态state的缺陷。笔者认为,react-hooks思想和初衷,也是把组件,颗粒化,单元化,形成独立的渲染环境,减少渲染次数,优化性能。

还不明白react-hooks的伙伴可以看的另外一篇文章: react-hooks如何使用?

什么是自定义hooks

自定义hooks是在react-hooks基础上的一个拓展,可以根据业务需要制定满足业务需要的hooks,更注重的是逻辑单元。通过业务场景不同,我们到底需要react-hooks做什么,怎么样把一段逻辑封装起来,做到复用,这是自定义hooks产生的初衷。

如何设计一个自定义hooks,设计规范

逻辑+ 组件


hooks 专注的就是逻辑复用, 是我们的项目,不仅仅停留在组件复用的层面上。hooks让我们可以将一段通用的逻辑存封起来。将我们需要它的时候,开箱即用即可。

自定义hooks-驱动条件

hooks本质上是一个函数。函数的执行,决定与无状态组件组件自身的执行上下文。每次函数的执行(本质上就是组件的更新)就会执行自定义hooks的执行,由此可见组件本身执行和hooks的执行如出一辙。

那么prop的修改,useState,useReducer使用是无状态组件更新条件,那么就是驱动hooks执行的条件。 我们用一幅图来表示如上关系。


自定义hooks-通用模式

我们设计的自定义react-hooks应该是长的这样的。


js 体验AI代码助手复制代码const [ xxx , ... ] = useXXX(参数A,参数B...)

在我们在编写自定义hooks的时候,要特别~特别~特别关注的是传进去什么,返回什么。 返回的东西是我们真正需要的。更像一个工厂,把原材料加工,最后返回我们。正如下图所示

自定义hooks-条件限定

如果自定义hooks没有设计好,比如返回一个改变state的函数,但是没有加条件限定限定,就有可能造成不必要的上下文的执行,更有甚的是组件的循环渲染执行。

比如:我们写一个非常简单hooks来格式化数组将小写转成大写。

jsx 体验AI代码助手复制代码
import React , { useState } from 'react'
/* 自定义hooks 用于格式化数组将小写转成大写 */
function useFormatList(list){
   return list.map(item=>{
       console.log(1111)
       return item.toUpperCase()
   })
}
/* 父组件传过来的list = [ 'aaa' , 'bbb' , 'ccc'  ] */
function index({ list }){
   const [ number ,setNumber ] = useState(0)
   const newList = useFormatList(list)
   return{ newList.map(item=>{ item }) }{ number }setNumber(number + 1) } >add}
export default index


如上述问题,我们格式化父组件传递过来的list数组,并将小写变成大写,但是当我们点击add。 理想状态下数组不需要重新format,但是实际跟着执行format。无疑增加了性能开销。

所以我们在设置自定义hooks的时候,一定要把条件限定-性能开销加进去。

于是乎我们这样处理一下。

js 体验AI代码助手复制代码function useFormatList(list) {
    return useMemo(() => list.map(item => {
        console.log(1111)
        return item.toUpperCase()
    }), [])
}


华丽丽的解决了如上的问题。

所以一个好用的自定义hooks,一定要配合useMemo ,useCallbackapi一起使用。

自定义hooks实战

准备工作:搭建demo样式项目

为了将实际的业务情景和自定义hooks连接在一起,我这里用 taro-h5 构建了一个移动端react项目。用于描述实际工作中用到自定义hooks的场景。

demo项目地址 : 自定义hooks,demo项目

后续会更新更多自定义hooks,或者感兴趣的同学可以关注一下这个项目,或者也可以一起维护这个项目。

项目结构


page文件夹里包括自定义hooks展示demo页面,hooks文件夹里面是自定义hooks内容。

展示效果

每个listItem记录每一个完成自定义hooks展示效果,陆续还有其他的hooks。我们接下来看看hooks具体实现。

实战一:控制滚动条-吸顶效果,渐变效果-useScroll

背景:公司的一个h5项目,在滚动条滚动的过程中,需要控制 渐变 + 高度 + 吸顶效果。

1实现效果


1 首先红色色块有吸顶效果。 2 粉色色块,是固定上边但是有少量偏移,加上逐渐变透明效果。

2 自定义useScroll设计思路

需要实现功能:

1 监听滚动条滚动。 2 计算吸顶临界值,渐变值,透明度。 3 改变state渲染视图。

好吧,接下来让我们用一个hooks来实现上述工作。

页面

jsx 体验AI代码助手复制代码import React from 'react'
import { View, Swiper, SwiperItem } from '@tarojs/components'
import useScroll from '../../hooks/useScroll'
import './index.less'
export default function Index() { 
    const [scrollOptions,domRef] = useScroll()
    /* scrollOptions 保存控制透明度 ,top值 ,吸顶开关等变量 */
    const { opacity, top, suctionTop } = scrollOptions
    return}

我们通过一个scrollOptions  来保存透明度 ,top值 ,吸顶开关等变量,然后通过返回一个ref作为dom元素的采集器。接下来就是hooks如果实现的。

useScroll

js 体验AI代码助手复制代码export default function useScroll() {
 const dom = useRef(null)
  const [scrollOptions, setScrollOptions] = useState({
    top: 0,
    suctionTop: false,
    opacity: 1
  })

  useEffect(() => {
    const box = (dom.current)
    const offsetHeight = box.offsetHeight
    const radio = box.offsetHeight / 500 * 20
    const handerScroll = () => {
      const scrollY = window.scrollY
      /* 控制透明度 */
      const computerOpacty = 1 - scrollY / 160
      /* 控制吸顶效果 */
      const offsetTop = offsetHeight - scrollY - offsetHeight / 500 * 84
      const top = 0 - scrollY / 5
      setScrollOptions({
        opacity: computerOpacty <= 0="">

有话要说...