在 React 中使用 refs

Avatar of Kingsley Silas
Kingsley Silas

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费额度!

Refs 使您能够直接在 React 中访问 DOM 节点。这在某些情况下非常有用,例如,当您想要更改组件的子元素时。假设您想更改 <input> 元素的值,但不想使用 props 或重新渲染整个组件。

这就是 refs 的用途,也是我们将在本文中深入探讨的内容。

更新:useRef 钩子

本文最初是在 React Hooks 出现之前编写的。如果您正在寻找这方面的信息,这里有一个快速示例

查看 CodePen 上的示例
React useRef 钩子
by CodePen (@codepen)
CodePen 上。

本文的其余部分将介绍 refs,这些内容在 refs 的用途和原因方面仍然相关。

如何创建 ref

createRef() 是一个新的 API,它与 React 16.3 一起发布。您可以通过调用 React.createRef() 并使用元素上的 ref 属性将其附加到 React 元素来创建 ref。

class Example extends React.Component {
  constructor(props) {
    super(props)

    // Create the ref
    this.exampleRef = React.createRef()
  }

  render() {
    return (
      <div>
        // Call the ref with the `ref` attribute
        <input type="text" ref={this.exampleRef} />
      </div>
    )
  }
}

我们可以使用对 ref 的 current 属性的访问来“引用”在 render 方法中创建的 ref 的节点。从上面的示例中,那就是 this.exampleRef.current

以下是一个示例

查看 CodePen 上的示例 React Ref – createRef by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

class App extends React.Component {
  constructor(props) {
    super(props)
    
    // Create the ref
    this.textInput = React.createRef();
    this.state = {
      value: ''
    }
  }
  
  // Set the state for the ref
  handleSubmit = e => {
    e.preventDefault();
    this.setState({ value: this.textInput.current.value})
  };

  render() {
    return (
      <div>
        <h1>React Ref - createRef</h1>
        // This is what will update
        <h3>Value: {this.state.value}</h3>
        <form onSubmit={this.handleSubmit}>
          // Call the ref on <input> so we can use it to update the <h3> value
          <input type="text" ref={this.textInput} />
          <button>Submit</button>
        </form>
      </div>
    );
  }
}
子组件和包含 ref 的元素之间可能发生的对话。

这是一个渲染一些文本、一个输入字段和一个按钮的组件。ref 在构造函数中创建,然后在渲染时附加到输入元素。当单击按钮时,从输入元素(已附加 ref)提交的值用于更新文本的状态(包含在 H3 标签中)。我们使用 this.textInput.current.value 来访问值,然后将新状态渲染到屏幕上。

将回调函数传递给 ref

React 允许您通过将回调函数传递给组件的 ref 属性来创建 ref。以下是其外观

<input type="text" ref={element => this.textInput = element} />

回调用于将对 DOM 节点的引用存储在实例属性中。当我们想要使用此引用时,我们使用以下方式访问它:

this.textInput.value

让我们看看它在我们之前使用的相同示例中的外观。

查看 CodePen 上的示例 React Ref – Callback Ref by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

class App extends React.Component {
    state = {
    value: ''
  }
  
  handleSubmit = e => {
    e.preventDefault();
    this.setState({ value: this.textInput.value})
  };

  render() {
    return (
      <div>
        <h1>React Ref - Callback Ref</h1>
        <h3>Value: {this.state.value}</h3>
        <form onSubmit={this.handleSubmit}>
          <input type="text" ref={element => this.textInput = element} />
          <button>Submit</button>
        </form>
      </div>
    );
  }
}

当您像上面那样使用回调时,React 会在组件挂载时使用 DOM 节点调用 ref 回调,在组件卸载时,它会使用 null 调用它。

还可以使用回调将 ref 从父组件传递到子组件。

查看 CodePen 上的示例 React Ref – Callback Ref 2 by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

让我们创建将渲染简单输入的“哑”组件

const Input = props => {
  return (
    <div>
      <input type="text" ref={props.inputRef} />
    </div>
  );
};

此组件正在从其父组件中获取 inputRef props,然后将其用于创建对 DOM 节点的 ref。

以下是父组件

class App extends React.Component {
  state = {
    value: ''
  };

  handleSubmit = event => {
    this.setState({ value: this.inputElement.value });
  };

  render() {
    return (
      <div>
        <h1>React Ref - Callback Ref</h1>
        <h3>Value: {this.state.value}</h3>
        <Input inputRef={el => (this.inputElement = el)} />
        <button onClick={this.handleSubmit}>Submit</button>
      </div>
    );
  }
}

在 App 组件中,我们希望获取在输入字段(位于子组件中)中输入的文本,以便我们可以渲染它。ref 使用回调创建,就像我们在本节的第一个示例中所做的那样。关键在于我们如何从 App 组件访问 Input 组件中输入元素的 DOM。如果您仔细观察,我们会使用 this.inputElement 访问它。因此,在更新 App 组件中 value 的状态时,我们使用 this.inputElement.value 获取在输入字段中输入的文本。

ref 属性作为字符串

这是创建 ref 的旧方法,由于与之相关的 一些问题,它可能会在将来的版本中被删除。React 团队建议不要使用它,甚至在文档中将其 标记为“遗留”。我们在这里仍然包含它,因为您可能在某个代码库中遇到它。

查看 CodePen 上的示例 React Ref – String Ref by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

回到我们输入值用于在提交时更新文本值的示例

class App extends React.Component {
    state = {
    value: ''
  }
  
  handleSubmit = e => {
    e.preventDefault();
    this.setState({ value: this.refs.textInput.value})
  };

  render() {
    return (
      <div>
        <h1>React Ref - String Ref</h1>
        <h3>Value: {this.state.value}</h3>
        <form onSubmit={this.handleSubmit}>
          <input type="text" ref="textInput" />
          <button>Submit</button>
        </form>
      </div>
    );
  }
}

组件被初始化,我们从默认状态值(设置为空字符串(value='’))开始。组件照常渲染文本和表单,并且像以前一样,当表单使用输入字段中输入的内容提交时,H3 文本更新其状态。

我们通过将输入字段的 ref prop 设置为 textInput 来创建 ref。这使我们能够使用 this.refs.textInput.valuehandleSubmit() 方法中访问输入的值。

将 ref 从一个组件转发到另一个组件

**Ref 转发是指通过使用 React.forwardRef() 方法将 ref 从组件传递到子组件的技术。**

查看 CodePen 上的示例 React Ref – forward Ref by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

回到我们正在运行的输入字段在提交时更新文本值的示例

class App extends React.Component {
    constructor(props) {
      super(props)
      this.inputRef = React.createRef();
      this.state = {
        value: ''
      }
    }
  
  handleSubmit = e => {
    e.preventDefault();
    this.setState({ value: this.inputRef.current.value})
  };

  render() {
    return (
      <div>
        <h1>React Ref - createRef</h1>
        <h3>Value: {this.state.value}</h3>
        <form onSubmit={this.handleSubmit}>
          <Input ref={this.inputRef} />
          <button>Submit</button>
        </form>
      </div>
    );
  }
}

我们已在此示例中使用 inputRef 创建了 ref,我们希望将其作为 ref 属性传递给子组件,以便我们可以使用它来更新文本的状态。

const Input = React.forwardRef((props, ref) => (
  <input type="text" ref={ref} />
));

以下是在 App 组件外部定义 ref 的另一种方法

const Input = React.forwardRef((props, ref) => (
  <input type="text" ref={ref} />
));

const inputRef = React.createRef();

class App extends React.Component {
    constructor(props) {
      super(props)
      
      this.state = {
        value: ''
      }
    }
  
  handleSubmit = e => {
    e.preventDefault();
    this.setState({ value: inputRef.current.value})
  };

  render() {
    return (
      <div>
        <h1>React Ref - createRef</h1>
        <h3>Value: {this.state.value}</h3>
        <form onSubmit={this.handleSubmit}>
          <Input ref={inputRef} />
          <button>Submit</button>
        </form>
      </div>
    );
  }
}

使用 ref 进行表单验证

我们都知道表单验证非常困难,但 React 非常适合处理它。例如,确保表单不能提交空输入值。或者要求密码至少包含六个字符。Refs 在这些情况下非常有用。

查看 CodePen 上的示例 React ref Pen – validation by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

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

    this.username = React.createRef();
    this.password = React.createRef();
    this.state = {
      errors: []
    };
  }

  handleSubmit = (event) => {
    event.preventDefault();
    const username = this.username.current.value;
    const password = this.password.current.value;
    const errors = this.handleValidation(username, password);

    if (errors.length > 0) {
      this.setState({ errors });
      return;
    }
    // Submit data
  };

  handleValidation = (username, password) => {
    const errors = [];
    // Require username to have a value on submit
    if (username.length === 0) {
      errors.push("Username cannot be empty");
    }
    
    // Require at least six characters for the password
    if (password.length < 6) {
      errors.push("Password should be at least 6 characters long");
    }
    
    // If those conditions are met, then return error messaging
    return errors;
  };

  render() {
    const { errors } = this.state;
    return (
      <div>
        <h1>React Ref Example</h1>
        <form onSubmit={this.handleSubmit}>
          // If requirements are not met, then display errors
          {errors.map(error => <p key={error}>{error}</p>)}
          <div>
            <label>Username:</label>
            // Input for username containing the ref
            <input type="text" ref={this.username} />
          </div>
          <div>
            <label>Password:</label>
            // Input for password containing the ref
            <input type="text" ref={this.password} />
          </div>
          <div>
            <button>Submit</button>
          </div>
        </form>
      </div>
    );
  }
}

我们使用 createRef() 为输入创建了 refs,然后将其作为参数传递给验证方法。当任何一个输入出现错误时,我们填充 errors 数组,然后将其显示给用户。

这就是一个 ref……或者说,一个总结!

希望这份指南能帮助您了解 refs 的强大功能。它们是更新组件一部分的绝佳方法,无需重新渲染整个组件。这对于编写更精简的代码和获得更好的性能非常方便。

同时,也值得注意 React 文档本身的建议,避免过度使用 ref

你最初的直觉可能是使用 refs 来“让事情在你的应用程序中发生”。如果是这样,请花一点时间更批判性地思考在组件层次结构中应该在哪里拥有状态。通常,很明显,“拥有”该状态的正确位置是在层次结构中较高的级别。

明白了吗?理解了?很好。