超性感的React Hooks(十)useRef

简介: 在函数式组件中,useRef 是一个返回可变引用对象的函数。该对象.current属性的初始值为useRef传入的参数initialVale。返回的对象将在组件整个生命周期中持续存在。const ref = useRef(initialValue);通常情况下,useRef有两种用途,•访问DOM节点,或者React元素•保持可变变量
import {useRef} from 'react';


在函数式组件中,useRef 是一个返回可变引用对象的函数。该对象.current属性的初始值为useRef传入的参数initialVale


返回的对象将在组件整个生命周期中持续存在。


const ref = useRef(initialValue);


通常情况下,useRef有两种用途,


访问DOM节点,或者React元素


保持可变变量

1


访问DOM节点或React元素


尽管使用React时,我们推荐大家仅仅只关注数据,但也存在一些场景,我们需要去访问DOM节点才能达到目的。例如下面这个例子。


import React, {Component, createRef} from "react";
export default class Demo extends Component {
  textInput = createRef<HTMLInputElement>();
  focusTextInput = () => {
    if (this.textInput.current) {
      this.textInput.current.focus();
    }
  }
  render() {
    return (
      <>
        <input type="text" ref={this.textInput} />
        <button onClick={this.focusTextInput}>点击我让input组件获得焦点</button>
      </>
    );
  }
}


案例很简单,通过点击别的按钮,让input组件获得焦点。


在函数组件中,我们可以通过useRef达到同样的目的。


import React, {useRef} from "react";
export default function Demo() {
  const inputRef = useRef<HTMLInputElement>(null);
  const focusTextInput = () => {
    if (inputRef.current) {
      inputRef.current.focus();
    }
  }
  return (
    <>
      <input type="text" ref={inputRef} />
      <button onClick={focusTextInput}>点击我让input组件获得焦点</button>
    </>
  );
}


这个例子使用useRef替换掉了createRef。


接下来思考一个问题,默认支持的input组件拥有.focus方法,调用该方法,input组件就能够获得焦点。那如果我们自己要封装一个Input组件,并且也希望该Input组件能够拥有.focus.blur方法,我们应该怎么办?


利用React提供的 api forwardRef就能够达到这个目的。forwardRef方法能够传递ref引用,具体使用如下


// 官网的案例
const FancyButton = React.forwardRef((props, ref) => (
  <button ref={ref} className="FancyButton">
    {props.children}
  </button>
));
// 你可以直接获取 DOM button 的 ref:
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;


我们也可以使用同样的方式,自定义Input组件。


import React, {forwardRef, useState, ChangeEvent} from 'react';
export interface InputProps {
  value?: string,
  onChange?: (value: string) => any
}
function Input({value, onChange}: InputProps, ref: any) {
  const [_value, setValue] = useState(value || '');
  const _onChange = (e: ChangeEvent<HTMLInputElement>) => {
    const value = e.target.value;
    setValue(value);
    onChange && onChange(value);
  }
  return (
    <div>
      自定义Input组件
      <input value={_value} onChange={_onChange} ref={ref} />
    </div>
  );
}
export default forwardRef(Input);


如果我们想要给.focus改个名字,或者返回其他额外的属性或者方法,我们可以使用useImperativeHandle


useImperativeHandle可以让我们在使用ref时自定义暴露给父组件的实例值。


import React, {useRef, useImperativeHandle, forwardRef, Ref, useState, ChangeEvent} from 'react';
export interface InputProps {
  value?: string,
  onChange?: (value: string) => any
}
export interface XInput {
  focus: () => void;
  blur: () => void;
  sayHi: () => void
}
function Input({value, onChange}: InputProps, ref: Ref<XInput>) {
  const inputRef = useRef<HTMLInputElement>(null);
  const [_value, setValue] = useState(value || '');
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current && inputRef.current.focus()
    },
    blur: () => {
      inputRef.current && inputRef.current.blur()
    },
    sayHi: () => {
      console.log('hello, world!');
    }
  }));
  const _onChange = (e: ChangeEvent<HTMLInputElement>) => {
    const value = e.target.value;
    console.log(value);
    setValue(value);
    onChange && onChange(value);
  }
  return (
    <div>
      自定义Input组件
      <input value={_value} onChange={_onChange} ref={inputRef} />
    </div>
  );
}
export default forwardRef(Input);


使用一下这个Input组件。


import React, { useRef, useState } from "react";
import Input from './components/Input';
import { Button } from "antd-mobile";
const Demo = () => {
  const textInput = useRef<any>(null);
  const [text, setText] = useState('')
  const focusTextInput = () => {
    if (textInput.current) {
      textInput.current.focus();
      textInput.current.sayHi();
    }
  }
  return (
    <>
      <Input ref={textInput} onChange={setText} value={text} />
      <Button onClick={focusTextInput}>点击我,input组件获得焦点</Button>
      <div>{text}</div>
    </>
  );
}
export default Demo;


2


在函数组件中,因为每次re-render就意味着函数重新执行一次,因此在函数内部保持变量引用是一件我们需要思考的事情。


在前面学习useState时我们知道,使用useState定义变量,可以做到这样的事情,同样的,利用ref的.current,也可以。


一个很常见的应用场景就是对于定时器的清除。我们需要确保setInterval的执行结果timer的引用,才能准确的清除对应的定时器。


import React, { useRef, useEffect } from 'react';
export default function Timer() {
  const timerRef = useRef<NodeJS.Timeout>();
  useEffect(() => {
    timerRef.current = setInterval(() => {
      console.log('do something');
    }, 1000);
    // 组件卸载时,清除定时器
    return () => {
      timerRef.current && clearInterval(timerRef.current);
    }
  }, []);
  return (
    <div>
      // ...
    </div>
  )
}


和useState不同,如果一个状态或者数据会影响DOM的渲染结果,一定要避免使用useRef来保持引用

3


通过ref访问DOM节点,除了配合useRef之外,仍然可以使用回调的形式获取。


<input type="text" ref={(node) => input = node} />


但是在函数组件中,由于我们还要思考如何使用一个引用稳定的变量来关联节点,这会比直接使用useRef更麻烦。因此,函数组件中推荐优先使用useRef


微信图片_20220510143922.jpg

相关文章
|
2月前
|
前端开发 JavaScript 开发者
深入理解React Hooks:提升前端开发效率的关键
【10月更文挑战第5天】深入理解React Hooks:提升前端开发效率的关键
|
2月前
|
前端开发 JavaScript
React Hooks 全面解析
【10月更文挑战第11天】React Hooks 是 React 16.8 引入的新特性,允许在函数组件中使用状态和其他 React 特性,简化了状态管理和生命周期管理。本文从基础概念入手,详细介绍了 `useState` 和 `useEffect` 的用法,探讨了常见问题和易错点,并提供了代码示例。通过学习本文,你将更好地理解和使用 Hooks,提升开发效率。
74 4
|
2月前
|
前端开发
深入解析React Hooks:构建高效且可维护的前端应用
本文将带你走进React Hooks的世界,探索这一革新特性如何改变我们构建React组件的方式。通过分析Hooks的核心概念、使用方法和最佳实践,文章旨在帮助你充分利用Hooks来提高开发效率,编写更简洁、更可维护的前端代码。我们将通过实际代码示例,深入了解useState、useEffect等常用Hooks的内部工作原理,并探讨如何自定义Hooks以复用逻辑。
|
2月前
|
前端开发 JavaScript API
探索React Hooks:前端开发的革命性工具
【10月更文挑战第5天】探索React Hooks:前端开发的革命性工具
|
21天前
|
前端开发 JavaScript API
探究 React Hooks:如何利用全新 API 优化组件逻辑复用与状态管理
本文深入探讨React Hooks的使用方法,通过全新API优化组件逻辑复用和状态管理,提升开发效率和代码可维护性。
|
24天前
|
前端开发
深入探索React Hooks:从useState到useEffect
深入探索React Hooks:从useState到useEffect
21 3
|
29天前
|
前端开发 JavaScript
深入探索React Hooks:从useState到useEffect
深入探索React Hooks:从useState到useEffect
|
1月前
|
前端开发 JavaScript 开发者
“揭秘React Hooks的神秘面纱:如何掌握这些改变游戏规则的超能力以打造无敌前端应用”
【10月更文挑战第25天】React Hooks 自 2018 年推出以来,已成为 React 功能组件的重要组成部分。本文全面解析了 React Hooks 的核心概念,包括 `useState` 和 `useEffect` 的使用方法,并提供了最佳实践,如避免过度使用 Hooks、保持 Hooks 调用顺序一致、使用 `useReducer` 管理复杂状态逻辑、自定义 Hooks 封装复用逻辑等,帮助开发者更高效地使用 Hooks,构建健壮且易于维护的 React 应用。
34 2
|
2月前
|
前端开发 开发者
React 提供的其他重要 Hooks
【10月更文挑战第20天】React 提供了一系列强大的 Hooks,除了 `useRef` 之外,还有许多其他重要的 Hooks,它们共同构成了函数式组件开发的基础。
37 6
|
2月前
|
存储 缓存 JavaScript
深入理解 React-Hooks 之 useRef
【10月更文挑战第20天】总之,`useRef` 是 React-Hooks 中一个非常实用的工具,它为我们提供了一种灵活而强大的方式来处理组件中的各种数据和操作。通过深入理解和掌握 `useRef` 的原理和用法,我们能够更好地构建高效、稳定的 React 应用。
42 6