React实现Vue的watch监听属性

1,281 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

前言

在Vue中可以简单地使用watch来监听数据的变化,还能获取到改变前的旧值,而在React中比较复杂,所以想在React中实现一个类似Vuewatch监听属性,咱们用自定义Hook在实现吧。

自定义Hook函数开头约定为 use,所以组件命名为 useWatch

image.png

1.创建一个useWatch.js文件

旧值咱们就用useRef来进行存储,它可以很方便地保存任何可变值 官网

useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内持续存在。

    import { useEffect, useRef } from 'react'
    
    // value 是要监听的属性   fn 是回调函数
    const useWatch = (value, fn) => {
      // 存储旧值
      const oldValue = useRef()
      useEffect(() => {
        fn(value, oldValue.current)
        oldValue.current = value
      }, [value])
    }

    export default useWatch

2.实现组件初次渲染不执行fn回调函数

useEffect会在初次渲染时会调用一次,所以要限制一下fn回调函数。

    import { useEffect, useRef } from 'react'
    
    const useWatch = (value, fn) => {
      const oldValue = useRef()
      // 声明一个变量限制初次渲染 fn 函数
      const isFirst = useRef(false)
      useEffect(() => {
      
      // 判断是否是初次渲染,初次渲染就不执行 fn 回调函数
      if(isFirst.current) {
          fn(value, oldValue.current)
      }else {
          isFirst.current = true
      }
        
      oldValue.current = value
      }, [value])
    }
    
    export default useWatch

3.添加 immediate 配置控制组件

Vue的watch是可以通过immediate来控制是否初次渲染后就执行回调函数,所以咱们也加一下。

    import { useEffect, useRef } from 'react'

    const useWatch = (value, fn, config = { immediate: false }) => {
      const oldValue = useRef()
      const isFirst = useRef(false)
      useEffect(() => {
        if (isFirst.current) {
          fn(value, oldValue.current)
        } else {
          isFirst.current = true

          // 是否要立即执行 fn 回调函数
          if (config.immediate) {
            fn(value, oldValue.current)
          }
        }

        oldValue.current = value
      }, [value])
    }

    export default useWatch

4.使用useWatch

监听普通类型或者复杂类型都是没问题的

    import { useState } from 'react'
    import useWatch from "./Hook/useWatch";
    
    export default function HelloWorld() {
      const [name, setName] = useState('豆豆')
      const [obj, setObj] = useState({name: '胡歌', age: 18})
      
      // 当name变化后会执行回调函数
      useWatch(name, (newName, oldName) => {
        console.log('newName', newName); // 一只豆豆
        console.log('oldName', oldName); // 豆豆
      })
      useWatch(obj, (newObj, oldObj) => {
        console.log('newObj', newObj); // {name: '彭于晏', age: 18}
        console.log('oldObj', oldObj); // {name: '胡歌', age: 18}
      })
      const changeName = () => {
        setName('一只豆豆')
        setObj({
            ...obj,
            name: '彭于晏'
        })
      };
      return (
        <>
          <button onClick={changeName}>click my</button>
        </>
      );
    }

打印效果图

image.png

结语

有帮助的话,给个赞哈。如果有错误欢迎在评论区指出。