Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refs #5

Open
yujihu opened this issue May 22, 2020 · 0 comments
Open

Refs #5

yujihu opened this issue May 22, 2020 · 0 comments

Comments

@yujihu
Copy link
Owner

yujihu commented May 22, 2020

Refs提供了一种方式,允许我们访问DOM节点或在render方法中创建的React组件。

创建Refs

使用React.createRef()创建Refs,并通过元素的ref属性附加到元素上。在构造组件的时候,通常会将Refs分配给组件实例的某个属性,以便可以在整个组件中引用它们。

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

访问Refs

当ref被传递给render中的元素时,可以通过ref的current属性访问节点的引用。

const node = this.myRef.current;

ref的值根据节点的类型会有所不同:

  • 当节点是HTML元素时,ref接收底层DOM元素作为其current属性;
  • 当节点时class组件时,ref接收组件的挂载实例作为其current属性;
  • 不能在函数组件上使用ref,因为它根本就没有实例。

Refs与函数组件:

默认情况下,我们不能在函数组件上使用ref属性,因为它们没有实例。如果要在函数组件上使用ref,我们可以使用Refs转发,将其指向一个DOM元素或class组件。

回调Refs

传递一个函数作为元素的ref属性,这个函数接收HTML DOM元素或React Class组件实例作为参数。

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

  setTextInputRef(element) {
    this.textInput = element;
  }

  render() {
    return (
      <div>
        <input
          type="text"
          ref={this.setTextInputRef}
        />
      </div>
    )
  }
}

可以在组件间传递回调形式的Refs,就像我们也可以传递通过React.createRef()创建的对象一样:

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

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

在上面例子中,Parent中的this.inputElement会被设置为与 CustomTextInput 中的 input 元素相对应的 DOM 节点。

转发Refs

Refs转发是将ref自动地通过组件传递到某个子组件的技巧。子组件通过React.forwardRef来获取传递给它的ref,然后转发给它内部的DOM元素或组件实例。

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>;

上面示例发生情况的逐步解释为:

  • 1.我们通过调用 React.createRef 创建了一个 React ref 并将其赋值给 ref 变量。
  • 2.我们通过指定 ref 为 JSX 属性,将其向下传递给 <FancyButton ref={ref}>
  • 3.React 传递 ref 给 forwardRef 内函数 (props, ref) => ...,作为其第二个参数。
  • 4.我们向下转发该 ref 参数到 <button ref={ref}>,将其指定为 JSX 属性。
  • 5.当 ref 挂载完成,ref.current 将指向 <button> DOM 节点。

注意:第二个参数 ref 只在使用 React.forwardRef 定义组件时存在。常规函数和 class 组件不接收 ref 参数,且 props 中也不存在 ref。

useRef

返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内保持不变。

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    // `current` 指向已挂载到 DOM 上的文本输入元素
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

useImperativeHandle

可以在我们使用Refs时自定义暴露给父组件的值,与React.forwardRef搭配使用。

const FancyInput = React.forwardRef((props, ref) => {
  const inputRef = useRef();
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    }
  }));
  return <input ref={inputRef} type="text" />;
});

渲染 <FancyInput ref={inputRef} /> 的父组件可以调用 inputRef.current.focus()

非受控组件

通常情况下,我们使用受控组件来处理表单数据。在一个受控组件中,表单数据是由React组件来管理的。而非受控组件的数据是由DOM节点来控制的。

在使用非受控组件时,我们可以使用ref来从DOM节点中获取表单数据:

class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.handleSubmit = this.handleSubmit.bind(this);
    this.input = React.createRef();
  }

  handleSubmit(event) {
    alert('A name was submitted: ' + this.input.current.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Name:
          <input type="text" ref={this.input} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant