• React:Refs and DOM


    React的哲学是在JS层面构建UI,并把UI组件以功能单位拆分成更小的组件单元,以利于复用和整合,父组件通过props作为操作子组件的唯一通道,甚至子组件想和父组件交互时,也只能依靠父组件通过props传递下来的方法(该方法在父组件内定义,子组件只是拿到它的引用)。在文档中,称这种模式属于dataflow,即把程序分成多个块,每个块相对独立,并有各自的处理逻辑(参考维基的,暂且这么理解)。

    在React中,考虑到有些场景通过直接获取元素的引用会更方便简洁,于是提供了Ref这个接口。Ref可以帮我们获取React Element或DOM Element的引用,在文档提到:

    When to Use Refs

    There are a few good use cases for refs:

    • Managing focus, text selection, or media playback.  //操作元素的焦点、文本选择和媒体播放
    • Triggering imperative animations.                              //触发必要的动画效果
    • Integrating with third-party DOM libraries.                 //整合进第三方的DOM插件时

    可以看出,这些场景都是想获取文档中的某个元素并触发摸个行为。在组件构建起来变得复杂时,可能元素众多,要获取/改变某个特定元素,要么经过几层筛选,要么传递的props变得臃肿,ref就像一个id,提供了简洁的接口和回调机制。

    同时,文档也提醒不要滥用ref,推荐只作为一种辅佐手段。

    Avoid using refs for anything that can be done declaratively.

    For example, instead of exposing open() and close() methods on a Dialog component, pass an isOpen prop to it.

    ref使用:

    第一个例子,ref将DOM元素的引用存储到组件的一个内部变量中。

     1   focus() {
     2     // Explicitly focus the text input using the raw DOM API
     3     this.textInput.focus();
     4   }
     5 
     6   render() {
     7     // Use the `ref` callback to store a reference to the text input DOM
     8     // element in an instance field (for example, this.textInput).
     9     return (
    10       <div>
    11         <input
    12           type="text"
    13           ref={(input) => { this.textInput = input; }} />
    14         <input
    15           type="button"
    16           value="Focus the text input"
    17           onClick={this.focus}
    18         />
    19       </div>
    20     );
    21   }

    在line13中,ref作为一个html特性插入到input中,它的值是一个函数。该函数在组件挂载的时候触发,函数的唯一参数是组件本身(也就是这个input元素),函数体内用一个变量this.textInput指向了该input。

    在组件从文档中卸载的时候,函数会再次触发,此时参数是null。也即是将this.textInput赋值为null,随后被js的垃圾收集机制清除。这是文档推荐的方式,单纯获取一个元素的引用。

    第二个例子,ref将自定义class组件的引用存储到组件的一个内部变量中。

     1 class AutoFocusTextInput extends React.Component {
     2   componentDidMount() {
     3     this.textInput.focus();
     4   }
     5 
     6   render() {
     7     return (
     8       <CustomTextInput
     9         ref={(input) => { this.textInput = input; }} />
    10     );
    11   }
    12 }

    当ref所在的是一个自定义的class组件时,其函数参数是class组件本身。

    第二个例子,ref不能用在Functional组件上,因为他们不会生成组件实例,一般而言他们只是返回JSX。只有class组件会生成带props和state的组件实例。

     1 function MyFunctionalComponent() {
     2   return <input />;
     3 }
     4 
     5 class Parent extends React.Component {
     6   render() {
     7     // This will *not* work!
     8     return (
     9       <MyFunctionalComponent
    10         ref={(input) => { this.textInput = input; }} />
    11     );
    12   }
    13 }

    除非把functional component 转换成classcomponent

    但是,我们可以在functional组件的内部使用ref,这和上面是不一样的:

     1 function CustomTextInput(props) {
     2   // textInput must be declared here so the ref callback can refer to it
     3   let textInput = null;
     4 
     5   function handleClick() {
     6     textInput.focus();
     7   }
     8 
     9   return (
    10     <div>
    11       <input
    12         type="text"
    13         ref={(input) => { textInput = input; }} />
    14       <input
    15         type="button"
    16         value="Focus the text input"
    17         onClick={handleClick}
    18       />
    19     </div>
    20   );  
    21 }

    要注意:

    这里的ref将当前的input传给了function内部的一个变量,所以function内部的handle方法可以引用到它。

    而前面的例子中,<MyFunctionalComponent>只是function返回到Parent组件内的一段JSX,其ref将input传给了Parent内部的变量。

     ----------------------------------------------------------------

    特殊场景:

            当我们想在父组件中获取子组件的某个DOM节点时~~~~~虽然这是不推荐的,但是React还是提供了一种方式:

            将一个ref回调函数作为props传给子组件,并在子组件的目标节点中作为元素的ref的回调。

     1 function CustomTextInput(props) {
     2   return (
     3     <div>
     4       <input ref={props.inputRef} />
     5     </div>
     6   );
     7 }
     8 
     9 class Parent extends React.Component {
    10   render() {
    11     return (
    12       <CustomTextInput
    13         inputRef={el => this.inputElement = el}
    14       />
    15     );
    16   }
    17 }

    这时候,子元素中的目标节点就作为父组件ref函数的参数,并被传给父组件的内部变量this.inputElement。

    这种方式对functional 组件返回的JSX也有效,毕竟获取元素引用的变量在父组件内部。这种方式还可以隔代传递,即父组件将{el => this.inputElement = el}传递给子组件,再由子组件传递给孙组件使用。

    注意:

    Legacy API: String Refs

    在以往的React中使用ref时,用的是字符串形式,比如:

    <input ref='textInp'>

    在组件内部可以使用this.refs.textInp获取对元素的引用(这是我在阮一峰先生几年前的博客中看到的)。

    但是现在React不再推荐这种用法,并言明在以后的某个版本中将会取缔这种使用方式,说是有着某些问题。some issues

    另外,ref的回调作为行内的function时,在渲染时可能会触发两次,第一次是旧组件销毁,传入null作为参数;第二次是新组件生成,同时也生成新的ref,传入组件本身作为参数。我们可以把ref回调作为class的内部方法绑定之。这样一来,ref回调不并重新建立,因为每次都是引用class内部的方法。

            

  • 相关阅读:
    C# 实现向指定邮箱发送信息功能
    asp.net webapi 解决跨域问题
    电脑通电自动开机设置
    C# 多个控件绑定一个事件
    C# DataGridView 标题栏背景颜色改变
    C# 输出csv文件后缀乱码
    C# textbox设定为只读后如何改变文本字体颜色
    C# 命名规则
    C# 傅里叶变换 逆变换 调用MathNet包
    使用SharpDevelop配合MonoGame进行游戏开发
  • 原文地址:https://www.cnblogs.com/alan2kat/p/7477937.html
Copyright © 2020-2023  润新知