[React翻译]Refs and the DOM

通常状况在一个正常的dataflow中,父组件老是将data经过props传给子组件,而后子组件re-render。可是有的时候咱们想当即修改一个react子组件或一个DOM,那该怎么办呢?node

对的,就是用refsreact

refs提供了一个DOM节点或react组件实例的引用,这样咱们在组件中就能访问到那个DOM节点或react组件实例dom

何时用refs

  • 处理focus,text 选择,或者视频回放
  • 触发交互式动画
  • 整合第三方DOM库

不要使用ref去作能够声名的事情。比方说不要让Dialog组件暴露open()和close()方法,而是使用props.isOpen去管理Dialog函数

建立refs

用React.createRef()建立refs的,而后经过ref属附加给一个React元素或者DOM。在构造组件时,一般将Refs分配给实例属性,以即可以在整个组件中引用它们。动画

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();
  }
  render() {
    return <div ref={this.myRef} />; } } 复制代码

访问refs

在生命周期方法或者事件处理器中可使用 current 属性访问refthis

const node = this.myRef.current
复制代码
  • 若是 ref 是给HTML元素用的,那么this.myRef.current 就是一个DOM
  • 若是ref 是给react组件用的,那么this.myRef.current 就是当前组件的实例
  • ref 不能用在函数式组件中,由于函数式组件没有实例

给DOM加ref

class CustomTextInput extends React.Component {
  constructor(props) {
    super(props);
    // 1. 首先建立ref
    this.textInput = React.createRef();
    this.focusTextInput = this.focusTextInput.bind(this);
  }

  focusTextInput() {
    // 3. 最后,咱们在事件处理器中就能使用原生的DOM方法了
    this.textInput.current.focus();
  }

  render() {
    // 2. 而后,告诉react咱们但愿把构造器里定义的textInput 指向 <input>
    return (
      <div>
        <input
          type="text"
          ref={this.textInput} />
        <input
          type="button"
          value="Focus the text input"
          onClick={this.focusTextInput}
        />
      </div>
    );
  }
}
复制代码
  1. 组件mount的时候current会被赋值为DOM,固然了还没mount的时候是null。
  2. ref会在componentDidMount or componentDidUpdate 以前更新。

给class组件加ref

比方说咱们想要一个AutoFocusTextInput组件,这个组件就是在mount以后调一下CustomTextInput的focusTextInput()方法:spa

class AutoFocusTextInput extends React.Component {
  constructor(props) {
    super(props);
    this.textInput = React.createRef();
  }

  componentDidMount() {
    this.textInput.current.focusTextInput();
  }

  render() {
    return (
      <CustomTextInput ref={this.textInput} /> ); } } 复制代码

注意CustomTextInput 必须是class型组件code

给function组件加ref

render的时候不能给一个function组件加refcomponent

function MyFunctionComponent() {
  return <input />;
}

class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.textInput = React.createRef();
  }
  render() {
    // This will *not* work!
    return (
      <MyFunctionComponent ref={this.textInput} />
    );
  }
}
复制代码

可是一个function组件内部仍是能用ref的,只要这个ref不是attach到function组件的视频

function CustomTextInput(props) {
  // textInput must be declared here so the ref can refer to it
  let textInput = React.createRef();

  function handleClick() {
    textInput.current.focus();
  }

  return (
    <div>
      <input
        type="text"
        ref={textInput} />
      <input
        type="button"
        value="Focus the text input"
        onClick={handleClick}
      />
    </div>
  );
}
复制代码

暴露DOM refs给父组件

想不想在父组件里面访问一个子DOM节点呢?

不,你不想。

由于这破坏了组件的封装。

但有些时候仍是能够用的,好比说触发一个focus或者测量DOM节点的大小和属性。

上面给class组件加ref的例子,有的时候也不是最理想的,由于这个时候返给咱们的是一个react组件,而不是一个DOM。

forward refs能让组件选择暴露哪些子组件的refs供本身用。

建议不要暴露DOM节点,但它能够是一个escape hatch。这个方法要向子组件添加一些代码。而后若是咱们彻底没法控制子组件实现,最后一个选项是使用findDOMNode(),但在StrictMode中是不能用的。

回调refs

class CustomTextInput extends React.Component {
  constructor(props) {
    super(props);

    this.textInput = null;

    this.setTextInputRef = element => {
      this.textInput = element;
    };

    this.focusTextInput = () => {
      // Focus the text input using the raw DOM API
      if (this.textInput) this.textInput.focus();
    };
  }

  componentDidMount() {
    // autofocus the input on mount
    this.focusTextInput();
  }

  render() {
    // Use the `ref` callback to store a reference to the text input DOM
    // element in an instance field (for example, this.textInput).
    return (
      <div>
        <input
          type="text"
          ref={this.setTextInputRef}
        />
        <input
          type="button"
          value="Focus the text input"
          onClick={this.focusTextInput}
        />
      </div>
    );
  }
}
复制代码

ref不只可使用React.createRef()建立,还可使用一种叫回调ref的方法实现。咱们给render函数里的dom加上一个回调ref,如上面的this.setTextInputRef。在挂载组件的时候react会调用回调ref,而后这个回调的参数会被react认为是当前的dom。这上面的例子中,回调ref,也就是this.setTextInputRef将这个dom的引用赋给了this.textInput。

组件之间能够传回调refs。

function CustomTextInput(props) {
  return (
    <div>
      <input ref={props.inputRef} />
    </div>
  );
}

class Parent extends React.Component {
  render() {
    return (
      <CustomTextInput
        inputRef={el => this.inputElement = el}
      />
    );
  }
}
复制代码

Parent给 inputRef prop 赋了一个回调,而后传给CustomTextInput,完了CustomTextInput又把这个回调给了input当作一个回调refs。这样的结果是啥呢?是Parent里头的this.inputElement会赋值为一个DOM,而这个DOM偏偏就是CustomTextInput里头的input

有回调refs的注意事项

ref 回调定义为内联函数的话更新的时候会被调用2次,1次refs是null,1次是DOM。这是由于每次渲染都会建立一个新的函数实例,因此react须要先清除旧的ref而后再设置新的ref。

能够把ref callback给bind到class上,不过大多数状况下这个事影响不大

相关文章
相关标签/搜索