react hooks系列之useRef

更新日期: 2019-10-25阅读: 2.5k标签: hooks

react hooks是 react 16.8 引入的特性,这里我们通过对react-hook-form进行分析来了解成熟的库是如何使用hook的。这将是一个系列,首先推荐 useRef


简介

在react中,我们使用Ref来获取组件的实例或者dom元素。我们可以使用两种方式来创建 Ref

import * as React from 'react'
import { useState, useEffect, useRef, createRef } from 'react'

export default () => {
    const ref1 = createRef<htmlFormElement>()
    const ref2 = useRef<HTMLInputElement>()

    useEffect(() => {
        console.log(ref1)
        console.log(ref2)
    }, [])

    return (
        <form ref={ref1}>
            <label>用户信息</label>
            <input type="text" ref={ref2} />
        </form>
    )
}

上面两种方式都能在组件 mounted 之后获取相关的DOM元素。在一个组件的正常的生命周期中可以大致可以分为3个阶段

  1. 从创建组件到挂载到DOM阶段。初始化props以及state, 根据state与props来构建DOM
  2. 组件依赖的props以及state状态发生变更,触发更新
  3. 销毁阶段

在第1阶段,使用createRef与useRef两者是没有区别。但是在第2阶段, 也就是更新阶段两者是有区别的。我们知道,在一个局部函数中,函数每一次执行,都会在把函数的变量重新生成一次。

export default () => {
    const ref1 = createRef<HTMLFormElement>()
    const ref2 = useRef<HTMLInputElement>()

    const [ count, setCount ] = useState<number>(0)

    useEffect(() => {
        if (!store.ref1) {
            store.ref1 = ref1
        } else {
            console.log(store.ref1 === ref1)
        }
    })
    
    useEffect(() => {
        setTimeout(() => {
            setCount(1)
        }, 1000)
    }, [])

    return (
        <form ref={ref1}>
            <label>用户信息</label>
            <input type="text" ref={ref2} />
        </form>
    )
}

我们使用一个外部的变量store来存储初次所创建的ref,在我们对组件进行更新后,会发现更新后的ref与我们初次创建的ref其实并不一致。这样也就意味着我们每更新一次组件, 就重新创建一次ref

由于有上面的问题,这在函数组件中,使用createRef去获取ref是不合理的。所以hook给我们提供一个新的api, 就是useRef。在useRef创建的ref仿佛就像外部定义的一个全局变量,不会随着组件的更新而重新创建。但组件销毁,它也会消失,不用手动进行销毁。

export default () => {
    const ref1 = createRef<HTMLFormElement>()
    const ref2 = useRef<HTMLInputElement>()

    const [ count, setCount ] = useState<number>(0)

    useEffect(() => {
        if (!store.ref1) {
            store.ref1 = ref1
        } else {
            console.log('ref1:', store.ref1 === ref1)
        }

        if (!store.ref2) {
            store.ref2 = ref2
        } else {
            console.log('ref2:', store.ref2 === ref2)
        }
    })

    useEffect(() => {
        setTimeout(() => {
            setCount(1)
        }, 1000)
    }, [])

    return (
        <form ref={ref1}>
            <label>用户信息</label>
            <input type="text" ref={ref2} />
        </form>
    )
}

通过上面的说明,我们知道useRef创建的ref并不会随着组件的更新而重新构建。由于这个特性,在使用react-hook的时候,可以使用useRef来存储常量。


useRef在react-hook-form中应用

现在回到我们的主题,看看react-hook-form是如何处理ref。在react-hook-form有一个API为register
源码实现如下

...
function register(refOrValidationOptions, validationOptions) {
        if (isWindowUndefined) {
            return;
        }
        if (isString(refOrValidationOptions)) {
            registerFieldsRef({ name: refOrValidationOptions }, validationOptions);
            return;
        }
        if (isObject(refOrValidationOptions) && 'name' in refOrValidationOptions) {
            registerFieldsRef(refOrValidationOptions, validationOptions);
            return;
        }
        return (ref) => ref && registerFieldsRef(ref, refOrValidationOptions);
    }

使用闭包存储了对当前输入框的validationOptions, 返回的函数被ref的接收。这里使用了ref另外一种获取方式‘回调refs’

import { useForm } from 'react-hook-form'


export default () => {
    const { register, errors, handleSubmit } = useForm()

    const submit = useCallback((data, e) => {
        console.log(data, e)
    }, [])

    useEffect(() => {
        console.log(errors)
    })
    
    return (
        <form onSubmit={handleSubmit(submit)}>
            <label>用户信息</label>
            <input name="userName" ref={register({ required: true })} />
            {errors.userName && "Your input is required"}

            <button type={'submit'}>提交</button>
        </form>
    )
}

这里为什么使用回调refs,而不是refs。其实理由很简单,因为后面要引用的DOM元素或者React实例是未知,我们是不知道使用者会把register注册到INPUTTEXTAREA、还是其他的第三方组件。注册一个还是多个。使用回调refs我们能够直接获取到对应的真实DOM元素或者React实例,而使用了refs就会失去这种灵活性。

如果我们继续往后面进行分析,会看到useForm这个hook中,使用了大量的useRef来存储变量,原因看前面。

而前面通过register中调用的ref对象被注册到filedsRef中。


链接: https://fly63.com/article/detial/8755

React将引入Hooks,你怎么看?

近日,据 MIT Technology Review 报道,一位名为“Repairnator”的机器人在 GitHub 上“卧底”数月,查找错误并编写和提交修复补丁,结果有多个补丁成功通过并被采纳,这位 Repairnator 到底是如何拯救程序员于水火的呢?

精通React今年最劲爆的新特性——React Hooks

你还在为该使用无状态组件(Function)还是有状态组件(Class)而烦恼吗?你还在为搞不清使用哪个生命周期钩子函数而日夜难眠吗?你在还在为组件中的this指向而晕头转向吗?这样看来,说React Hooks是今年最劲爆的新特性真的毫不夸张。

使用react hooks实现自己的context-redux

我们将userReducer函数返回的原始dispath命名为origin_dispatch,自定义dispatch函数,当action为函数的时候,我们执行action函数,并将origin_dispatch当作参数传进去;action不是函数,直接调用origin_dispatch,不做处理

useEffect Hook 是如何工作的?

使用useEffect 就像瑞士军刀。它可以用于很多事情,从设置订阅到创建和清理计时器,再到更改ref的值。与 componentDidMount、componentDidUpdate 不同的是,在浏览器完成布局与绘制之后,传给 useEffect 的函数会延迟调用。

React Hooks 你真的用对了吗?

从 React Hooks 正式发布到现在,我一直在项目使用它。但是,在使用 Hooks 的过程中,我也进入了一些误区,导致写出来的代码隐藏 bug 并且难以维护。这篇文章中,我会具体分析这些问题,并总结一些好的实践,以供大家参考

如何用 Hooks 来实现 React Class Component 写法?

Hooks 的 API 可以参照 React 官网。本文主要是结合 Demo 详细讲解如何用 Hooks 来实现 React Class Component 写法,让大家更深的理解 Hooks 的机制并且更快的入门。 注意:Rax 的写法和 React 是一致的

React-Hooks

以下是上一代标准写法类组件的缺点,也正是hook要解决的问题,型组件很难拆分和重构,也很难测试。业务逻辑分散在组件的各个方法之中,导致重复逻辑或关联逻辑。

React Hooks与setInterval

Hooks出来已经有段时间了,相信大家都用过段时间了,有没有小伙伴们遇到坑呢,我这边就有个 setInterval 的坑,和小伙伴们分享下解决方案。写个 count 每秒自增的定时器,如下写法结果,界面上 count 为 1 ?

React Hooks 底层解析[译]

对于 React 16.7 中新的 hooks 系统在社区中引起的骚动,我们都有所耳闻了。人们纷纷动手尝试,并为之兴奋不已。一想到 hooks 时它们似乎是某种魔法,React 以某种甚至不用暴露其实例

React Hooks实践

9月份开始,使用了React16.8的新特性React Hooks对项目进行了重构,果然,感觉没有被辜负,就像阮一峰老师所说的一样,这个 API 是 React 的未来。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!