为了账号安全,请及时绑定邮箱和手机立即绑定

React - Refs 和 DOM

标签:
React.JS

  在常规的 React 数据六中,props 是父组件与子组件交互的唯一方法。如果需要改子元素,你需要用新的props去重新渲染子元素。 然而,在少数情况下,你需要在常规数据流外强制修改子元素。被修改的子元素可以是React组件实例,或者是一个DOM元素。React团队提供了Refs办法

# 使用Refs 场景

  在如下几种情形中,我们可以引入refs

  • 处理 focus、文本选择或者媒体播放时

  • 触发强制动画时

  • 继承第三方DOM库时

注意:如果可以通过声明式实现,就尽量避免使用refs。例如一个场景。如果我们想在Dialog组件中暴露open()close()方法,则传递isOpen状态标志即可。也就是说,我们不应该过度的使用refs,通常较高级别的state更为清晰。

# 在元素本身的DOM元素上添加 Ref

  React 支持给任何组件添加特殊的属性。 ref属性接受回调函数,并且当组件 装载(mounted) 或者 卸载(unmounted) 之后,回调函数会立即执行。

  当给HTML元素添加 ref 属性时,ref回调接受底层的DOM 元素作为参数。例如,下面的代码使用ref 回调来存储DOM节点的引用

class CustomTextInput extends React.Component {
  constructor(props) {
    super(props);    this.focus = this.focus.bind(this);
  }

  focus() {    // 通过原生的API,显示地聚焦text输入框
    this.textInput.focus();
  }

  render() {    // 在实例中通过使用`ref`回调函数来存储text输入框的DOM元素引用(例如:this.textInput)
    return (
      <div>
        <input 
            type="text" 
            ref={(input) => { this.textInput = input; }} />
        <input 
            type="button" 
            value="Focus the text input" 
            onClick={this.focus} />
      </div>
    )  
  }
}

  React 组件在加载时将DOM元素传入ref 的回调函数,在写在时则会传入 null, 在 componentDidMountcomponentDidUpdate 这些生命周期回之前执行 ref回调

  使用ref 回调只是为了在类上设置一个属性,是访问DOM元素常见模式。 首选的方法是在 ref 回调中设置属性。就像上面的例子一样,他也可以写成ref={input => this.textInput = input}

# 为 类(class)组件添加 Ref

  当 ref 属性用于类声明的自定义组件时, ref回到上述收到的参数装载(mounted)的组件实例. 例如如果我们想包装 CustomTextInput 组件,实现组件在 装载(mounted) 后立即点击的效果:

class AutoFocusTextInput extends React.Component {
  componentDidMount() {    this.textInput.focus();
  }

  render() {    return (      <CustomTextInput
        ref={(input) => { this.textInput = input; }} />
    );
  }
}

 需要注意的是,这种方法仅对以类声明的 CustomTextInput 有效,函数式声明的是无效的。

# Refs 与函数式组件

你不能在函数式组件上使用 ref 属性,因为它们没有实例

function MyFunctionalComponent() {  return <input />;
}class Parent extends React.Component {
  render() {    // 这里 *不会* 执行!
    return (
      <MyFunctionalComponent
        ref={(input) => { this.textInput = input; }} />
    );
  }
}

  如果你需要使用 ref ,你需要将组件转化成 类组件,就像需要 生命周期方法 或者 state 一样。然而你可以 在函数式组件内部使用ref 来引用一个 DOM 元素或者 类组件

function CustomTextInput(props) {  // textInput必须在这里声明,所以 ref 回调可以引用它
  let textInput = null;  function handleClick() {
    textInput.focus();
  }  return (
    <div>
      <input
        type="text"
        ref={(input) => { textInput = input; }} />
      <input
        type="button"
        value="Focus the text input"
        onClick={handleClick}
      />
    </div>
  );  
}

# 对父组件暴露 DOM 节点

  在极少数的情况下,你可能希望从父组件中访问子节点的DOM节点。这种情况不被推崇使用,因为他会破坏组件的封装,丢失子组件独立工作的效果。但偶尔也可用于触发焦点或测量子 DOM 节点的大小或位置。

  虽然你可以向子组件添加 ref,但这不是一个理想的解决方案,因为你只能获取组件实例而不是 DOM 节点。并且,它还在函数式组件上无效。

  相反,在这种情况下,我们建议在子节点上暴露一个特殊的属性。子节点将会获得一个函数属性,并将其作为 ref 属性附加到 DOM 节点。这允许父代通过中间件将 ref回调给子代的 DOM 节点. 这适用于类组件和函数式组件。

function CustomTextInput(props) {  return (
    <div>
      <input ref={props.inputRef} />
    </div>
  );
}class Parent extends React.Component {
  render() {    return (
      <CustomTextInput
        inputRef={el => this.inputElement = el}
      />
    );
  }
}

  在上面的例子中,Parent 将它的ref 回调作为一个特殊的inputRef传递给 CustomTextInput,然后 CustomTextInput 通过ref属性将其传递给 <input>。最终,Parent中的 this.inputElement 将被设置为与 CustomTextInput中的 <input> 元素相对应的 DOM 节点。

  请注意,上述示例中的 inputRef 属性没有特殊的含义,它只是一般的组件属性。然而,使用<input>本身的ref 属性很重要,因为它告诉 Reactref 附加到它的 DOM 节点。

  即使 CustomTextInput 是一个函数式组件,它也同样有效。这里在父组件定义的是自定义属性,而不是特殊的 ref 因此不会受影响

  这种模式的另一个好处是它能作用很深。假如有个 Parent 组件不需要 DOM 节点 A,但是某个渲染 Parent 的组件(我们称之为Grandparent)需要通过它访问。这时我们可以让 Grandparent 传递 inputRefParent 组件,然后让Parent组件将其转发给 CustomTextInput

function CustomTextInput(props) {  return (
    <div>
      <input ref={props.inputRef} />
    </div>
  );
}function Parent(props) {  return (
    <div>
      My input: <CustomTextInput inputRef={props.inputRef} />
    </div>
  );
}class Grandparent extends React.Component {
  render() {    return (
      <Parent
        inputRef={el => this.inputElement = el}
      />
    );
  }
}

总而言之,我们建议尽可能不暴露 DOM 节点,但这是一个有用的解决方式。请注意,此方法要求您向子组件添加一些代码,如果你无法完全控制子组件,最后的办法是使用findDOMNode(),但是不推荐这样做。



作者:果汁凉茶丶
链接:https://www.jianshu.com/p/872e2af252e6


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消