likes
comments
collection
share

React hook的另类成道

作者站长头像
站长
· 阅读数 18

前景

众所周知大道三千,主流的React hook道法是 useCallback(函数)+useEffect(watch)+useRef(不监听的变量)+useState(状态独立管理)+useMemo(计算属性/jsx)。除了主流之外还有很多用法,今日就来说说一种你看了一定会喊666的道法

道法说明

事先声明:本道法经过业务的验证,在复杂业务中使用至今没啥问题,若道友们不甚走火入魔,概不负责🤪

进入正题,利用useRef的穿透特性,跳过函数的依赖编写过程,让开发者更加关注函数的逻辑实现,而不是分散一部分注意力在依赖上(即使vscode可以自动帮你补全),先直接上demo,然后再说下利弊

  • use-immer 谁用谁知道,这可是官网推荐的哦
  • antd ui库
import React, { useRef } from 'react'
import { useImmer } from 'use-immer'
import { Input, Select } from 'antd'

export default function Demo(props) {
  const [state, setState] = useImmer({
    formData: {
      type: 1,
      labelList: ['new', 'old', 'pre'],
    },
  })
  const { formData } = state
  const stateRef = useRef(null)
  stateRef.current = { ...state }

  const fnsRef = useRef({
    init() {
      setState((state) => {
        state.formData = initFormData()
      })
    },
    updateLabel(index, label) {
      const {
        formData: { type },
      } = stateRef.current
      if (type !== 1) {
        setState((state) => {
          state.labelList[index] = label
        })
      }
    },
  })
  const { updateLabel } = fnsRef.current
  
  useEffect(() => {
    fnsRef.current.init()
  }, [])

  return (
    <div>
      <Select
        value={formData.type}
        options={[
          { value: 1, label: '不可变' },
          { value: 2, label: '可变' },
        ]}
      />
      {formData.labelList.map((label, index) => (
        <Input
          value={label}
          key={index}
          onChange={(e) => updateLabel(e.target.value, index)}
        />
      ))}
    </div>
  )
}

看完demo,我想诸位应该有众多疑惑,接下来依依解答

  1. 使用use-immer代替useState,更新状态变得简单,而且渲染性能更优,useState看似把状态的变化都隔离开来,方便追踪,但是实际使用时候,一句debugger就完了。特别是复杂的状态更新,你可能要写一堆{...state}类似语句。
  2. 使用ref来定义函数代替useCallback,函数永远不存在像useCallback因为依赖变化而更新的情况,对渲染友好,写函数的时候,再也不用写一堆const xxx = useCallback(cb, deps)了,因为stateRef把最新的state存了下来,直接获取就行,不需要考虑依赖。个人觉得,函数就应该考虑逻辑的实现,而不是把许多精力放在依赖上面,这样写函数才会回归我们的简单存粹
  3. 正常来说,我们使用useEffect只是希望监听状态发生变化了才会执行某一个函数,但是呢因为hook里把函数useCallback也当成可一个变量来看待,所以还需要在依赖里加上这个函数变量,而这个函数可能又依赖于其他的我们不关心的变量,这就变成了我只想监听变量a、b变化时候,执行fn,而fn依赖于c、d,变相的整个依赖变成了四个。为了防止这种现象发生可能会直接禁止eslint去检查这一个useEffect。所以换一种写法,ref让我们跳过不想要的监听,让我们写代码时候更加专注于逻辑的实现

总结

总的来说,好处挺多,这种写法最不爽的地方就在于重复的写stateRef赋值和结构变量。没有完美的写法,只有合适的道法,诸位道友你心动了么🤔