2.6Refs


文档摘要

2.6Refs React Refs 详解:访问底层DOM与管理组件实例 在 React 开发中,我们通常遵循“数据驱动视图”的原则,通过 state 和 props 来控制组件的渲染。然而,在某些情况下,我们需要直接访问底层 DOM 元素或组件实例,以便执行一些特定的操作,例如: 直接操作 DOM 元素,如聚焦输入框、播放/暂停视频等。 访问子组件的公共方法。 集成第三方库,这些库可能需要直接操作 DOM 元素。 这时,React 的 Refs 就派上了用场。Refs 提供了一种访问 DOM 节点或 React 组件实例的方式,它就像一个“引用”,允许我们绕过 React 的虚拟 DOM,直接与真实 DOM 交互。 2.6.

2.6Refs

React Refs 详解:访问底层DOM与管理组件实例

在 React 开发中,我们通常遵循“数据驱动视图”的原则,通过 state 和 props 来控制组件的渲染。然而,在某些情况下,我们需要直接访问底层 DOM 元素或组件实例,以便执行一些特定的操作,例如:

  • 直接操作 DOM 元素,如聚焦输入框、播放/暂停视频等。

  • 访问子组件的公共方法。

  • 集成第三方库,这些库可能需要直接操作 DOM 元素。

这时,React 的 Refs 就派上了用场。Refs 提供了一种访问 DOM 节点或 React 组件实例的方式,它就像一个“引用”,允许我们绕过 React 的虚拟 DOM,直接与真实 DOM 交互。

2.6.1 Refs 的创建与使用

React 提供了多种创建 Refs 的方式,最常见的是使用 React.createRef()useRef() Hook。

2.6.1.1 使用 React.createRef() (Class 组件)

在 Class 组件中,我们可以使用 React.createRef() 方法创建一个 Ref 对象,并将其赋值给一个实例属性。然后,将该 Ref 对象绑定到要访问的 DOM 元素或组件上。

import React, { Component } from 'react'; class MyInput extends Component { constructor(props) { super(props); this.inputRef = React.createRef(); // 创建 Ref 对象 } componentDidMount() { // 组件挂载后,聚焦输入框 this.inputRef.current.focus(); } render() { return ( <input type="text" ref={this.inputRef} /> // 将 Ref 对象绑定到 input 元素 ); } } export default MyInput;

代码详解:

  1. this.inputRef = React.createRef();: 在构造函数中,我们使用 React.createRef() 创建了一个 Ref 对象,并将其赋值给 this.inputRef 实例属性。这个 Ref 对象最初是 null

  2. ref={this.inputRef}: 在 render() 方法中,我们将 this.inputRef 绑定到 <input> 元素上。 React 会将该 input 元素的 DOM 节点赋值给 this.inputRef.current

  3. this.inputRef.current.focus();: 在 componentDidMount() 生命周期方法中,我们可以通过 this.inputRef.current 访问到 input 元素的 DOM 节点,并调用其 focus() 方法使其获得焦点。

图示:

2.6.1.2 使用 useRef() Hook (函数组件)

在函数组件中,我们使用 useRef() Hook 创建 Ref 对象。 useRef() 返回一个具有 .current 属性的可变对象,该对象在组件的整个生命周期内保持不变。

import React, { useRef, useEffect } from 'react'; function MyInput() { const inputRef = useRef(null); // 创建 Ref 对象,初始值为 null useEffect(() => { // 组件挂载后,聚焦输入框 inputRef.current.focus(); }, []); // 依赖项为空数组,确保只在组件挂载时执行一次 return ( <input type="text" ref={inputRef} /> // 将 Ref 对象绑定到 input 元素 ); } export default MyInput;

代码详解:

  1. const inputRef = useRef(null);: 使用 useRef(null) 创建一个 Ref 对象,并将其赋值给 inputRef 变量。 初始值为 null,因为在组件首次渲染时,DOM 节点尚未创建。

  2. ref={inputRef}: 将 inputRef 绑定到 <input> 元素上。 React 会将该 input 元素的 DOM 节点赋值给 inputRef.current

  3. useEffect(() => { ... }, []);: 使用 useEffect Hook 在组件挂载后执行副作用。 useEffect 的第二个参数是一个空数组 [],这意味着该副作用只会在组件首次挂载时执行一次。

  4. inputRef.current.focus();: 在 useEffect 内部,我们可以通过 inputRef.current 访问到 input 元素的 DOM 节点,并调用其 focus() 方法使其获得焦点。

图示:

2.6.1.3 将 Refs 绑定到组件

除了绑定到 DOM 元素,Refs 还可以绑定到 React 组件。 这允许父组件访问子组件的实例方法。

import React, { Component, forwardRef, useImperativeHandle, useRef } from 'react'; // Class 组件 class MyInput extends Component { focus() { this.inputElement.focus(); } render() { return ( <input type="text" ref={(el) => this.inputElement = el} /> ); } } // 函数组件 - 使用 forwardRef 和 useImperativeHandle const FancyInput = forwardRef((props, ref) => { const inputRef = useRef(); useImperativeHandle(ref, () => ({ focus: () => { inputRef.current.focus(); } })); return <input ref={inputRef} /> }); class ParentComponent extends Component { constructor(props) { super(props); this.inputRef = React.createRef(); this.fancyInputRef = React.createRef(); } componentDidMount() { // 聚焦 MyInput 组件的 input 元素 this.inputRef.current.focus(); // 聚焦 FancyInput 组件的 input 元素 this.fancyInputRef.current.focus(); } render() { return ( <div> <MyInput ref={this.inputRef} /> <FancyInput ref={this.fancyInputRef} /> </div> ); } } export default ParentComponent;

代码详解:

  1. MyInput (Class 组件): 使用回调 Ref,将 input 元素的 DOM 节点赋值给 this.inputElement。 然后定义一个 focus 方法,调用 this.inputElement.focus()。 父组件可以通过 this.inputRef.current.focus() 调用该方法。

  2. FancyInput (函数组件):

    • forwardRef: 使用 forwardRef 高阶组件,将 ref 传递给函数组件。 forwardRef 接收一个渲染函数,该函数接收 propsref 作为参数。

    • useImperativeHandle: 使用 useImperativeHandle Hook 自定义暴露给父组件的实例值。 useImperativeHandle 接收 ref 和一个函数作为参数。 该函数返回一个对象,该对象包含了父组件可以通过 ref.current 访问的属性和方法。

    • 在这个例子中,useImperativeHandle 暴露了一个 focus 方法,该方法调用 inputRef.current.focus()。 父组件可以通过 this.fancyInputRef.current.focus() 调用该方法。

图示:

2.6.2 回调 Refs

除了使用 React.createRef()useRef(),还可以使用回调 Refs。 回调 Refs 允许你在组件挂载或卸载时执行自定义逻辑。

import React, { Component } from 'react'; class MyComponent extends Component { constructor(props) { super(props); this.myRef = null; // 初始化为 null } setMyRef = (element) => { this.myRef = element; // 在这里可以执行一些操作,例如: if (element) { console.log('DOM element is attached:', element); } } componentWillUnmount() { // 在组件卸载时,将 myRef 设置为 null this.myRef = null; console.log('Component is unmounting, ref is cleared.'); } render() { return ( <div ref={this.setMyRef}> This is my component. </div> ); } } export default MyComponent;

代码详解:

  1. this.myRef = null;: 在构造函数中,将 this.myRef 初始化为 null

  2. setMyRef = (element) => { ... }: 定义一个回调函数 setMyRef,该函数接收一个参数 element,该参数是 DOM 元素。

    • 当组件挂载时,React 会将 DOM 元素作为参数传递给 setMyRef 函数,并将 this.myRef 设置为该 DOM 元素。

    • 当组件卸载时,React 会将 null 作为参数传递给 setMyRef 函数,并将 this.myRef 设置为 null

  3. ref={this.setMyRef}: 将 setMyRef 函数作为 ref 属性的值传递给 <div> 元素。

  4. componentWillUnmount(): 在 componentWillUnmount 生命周期方法中,将 this.myRef 设置为 null,以确保在组件卸载后,不再持有对 DOM 元素的引用。

图示:

2.6.3 Refs 的注意事项

  • 避免过度使用 Refs: 尽量使用 React 的数据流机制 (state 和 props) 来控制组件的渲染。 只有在必要时才使用 Refs。

  • 不要在 render 方法中访问 Refs: 在 render 方法中访问 Refs 可能会导致性能问题,因为 render 方法可能会被频繁调用。 应该在 componentDidMountcomponentDidUpdate 或事件处理函数中访问 Refs。

  • 小心使用回调 Refs: 确保在组件卸载时清除回调 Refs,以避免内存泄漏。

  • 函数组件中使用 useRef Hook: 在函数组件中,应该使用 useRef Hook 来创建 Refs,而不是 React.createRef()

2.6.4 总结

Refs 是 React 提供的一种访问底层 DOM 元素或组件实例的方式。 通过 Refs,我们可以绕过 React 的虚拟 DOM,直接与真实 DOM 交互,执行一些特定的操作。 然而,应该谨慎使用 Refs,避免过度使用,并注意一些潜在的问题,例如性能问题和内存泄漏。 在大多数情况下,应该优先使用 React 的数据流机制 (state 和 props) 来控制组件的渲染。


发布者: 作者: 转发
评论区 (0)
U