内容简介:原文:作者:Yomi Eluwande译者:博轩
原文: Fullstack React's Guide to using Refs in React Components
作者:Yomi Eluwande
译者:博轩
使用 React
时,我们的默认思维方式应该是 不会强制修改 DOM
,而是通过传入 props
重新渲染组件。但是,有些情况却无法避免修改 DOM
。
React
中的 Refs
提供了一种访问 render()
方法中创建的 React
元素(或 DOM
节点)的方法。
当父组件需要与子组件交互时,我们通常使用 props 来传递相关信息。 但是, 在某些情况下,我们可能需要修改子项,而不用新的 props
重新呈现 (re-rendering) 它。 这时候就需要 refs
出场了。
我什么时候应该使用 Refs ?
我们建议在以下情况下使用 refs
:
DOM
译注:第三点是否也可以理解为使用 event
对象呢?在 React 中就是合成事件(SyntheticEvent)。
官方文档中提到:避免使用 refs
来做任何可以通过声明式实现来完成的事情。
所以一旦我们确定我们真的应该使用 refs
,我们需要如何使用它们呢?
在 React 中使用 Refs
您可以通过多种方式使用 refs
:
- React.createRef()
- 回调引用 (Callback refs)
- String refs(已过时)
- 转发
refs
(Forwarding refs)
接下来,让我们看看每一种实现方式:
React.createRef()
可以使用该 React.createRef()
函数创建 Refs
,并通过该 ref
属性附加到 React
组件中的 HTML
元素。
通常在组件的构造函数内创建 ref
,使其在整个组件中可用。例如:
class MyComponent extends React.Component { constructor(props) { super(props); this.firstRef = React.createRef(); } render() { return <div ref={this.firstRef} />; } }
如上所示:
- 一个
ref
实例在构造函数中创建,并赋值给this.firstRef
- 在
render()
方法内部,将构造函数中创建的ref
传递给div
接下来,让我们看一个在 React
组件中使用 refs
的示例。
使用 Refs
聚焦输入
这是另一个例子:
// Ref.js class CustomTextInput extends React.Component { constructor(props) { super(props); // create a ref to store the textInput DOM element this.textInput = React.createRef(); this.focusTextInput = this.focusTextInput.bind(this); } focusTextInput() { // Explicitly focus the text input using the raw DOM API // Note: we're accessing "current" to get the DOM node this.textInput.current.focus(); } render() { // tell React that we want to associate the <input> ref // with the `textInput` that we created in the constructor return ( <div> <input type="text" ref={this.textInput} /> <input type="button" value="Focus the text input" onClick={this.focusTextInput} /> </div> ); } }
在上面的代码块中,我们构建了一个按钮,当单击它时, 该页面会自动聚焦在输入框上。
首先,我们在构造方法中创建一个 ref
实例,并将其赋值给 this.textInput
,然后通过 ref
属性将其分配给 input
元素。
<input type="text" ref={this.textInput} />
注意,当 ref
属性被一个 HTML
元素使用时(比如当前示例中的 input
元素),在 constructor
中使用 React.createRef()
创建的 ref
会接收 来自底层 DOM
元素的 current
值。
译注:这里的 current 应该是 合成事件(SyntheticEvent)
这意味着访问 DOM
值,我们需要写这样的东西:
this.textInput.current;
第二个元素是一个按钮,点击它之后会自动聚焦到第一个输入框上面。我们为 onClick
属性设置了 this.focusTextInput
函数。
<input type="button" value="Focus the text input" onClick={this.focusTextInput} />
函数 focusTextInput()
使用了 JavaScript
构建 DOM
的标准函数。 .focus()
方法会将光标聚焦于文本输入框上。
focusTextInput() { this.textInput.current.focus(); }
最后, focusTextInput
函数绑定在这样的 constructor
方法中的:
this.focusTextInput = this.focusTextInput.bind(this);
从 ref
中获取值
在这个例子中,我们将看到如何为 input
输入框设置 ref
属性,并通过 ref
来获取值。示例如下:
在这个例子中,我们创建了一个 input
输入框来输入值。然后,当单击提交按钮时,我们将读取此值,并在控制台打印。
// Ref.js class CustomTextInput extends React.Component { constructor(props) { super(props); // create a ref to store the textInput DOM element this.textInput = React.createRef(); } handleSubmit = e => { e.preventDefault(); console.log(this.textInput.current.value); }; render() { // tell React that we want to associate the <input> ref // with the `textInput` that we created in the constructor return ( <div> <form onSubmit={e => this.handleSubmit(e)}> <input type="text" ref={this.textInput} /> <button>Submit</button> </form> </div> ); } }
同样,我们使用该 React.createRef()
函数创建一个 ref
实例,然后将它分配给实例变量 this.textInput
。
在 render
函数中,我们希望读取 form
下输入框的值。 我们如何读取这个值? 通过为 input
指定一个 ref
,然后读取 ref
的值。
<input type="text" ref={this.textInput} />
点击提交按钮,上面示例中 form
元素会通过 onSubmit
方法,调用 this.handleSubmit
函数 ,并在控制台打印输入框中的信息。
handleSubmit = e => { e.preventDefault(); console.log(this.textInput); };
e
包含事件对象。我们使用
e.preventDefault()
来告诉浏览器我们正在处理被点击的提交按钮,我们不希望这个事件“冒泡”(意思就是说,阻止浏览器的默认行为)。
译注:这里可以看一下 React 对于 事件的处理 :在 React 中另一个不同点是你不能通过返回 false 的方式阻止默认行为。你必须显式的使用 preventDefault
在上面示例中,我们打印了 this.textInput
,在控制台可以看到一个 ref
对象。
> Object {current: HTMLInputElement}
请注意,它有一个 current
属性,即 HTMLInputElement
。这是 input
DOM 元素本身, 而不是实际值。 我们必须使用 this.textInput.current.value
来获取 input
标签的实际值:
handleSubmit = e => { e.preventDefault(); console.log(this.textInput.current.value); };
使用 refs
是一种从表单中直接提取值的方式:只需要给 input
标签设置 ref
,并在你需要的时候将值提取出来。
Refs 回调
Refs 回调是在 React
中使用 ref
的另一种方式。要以这种方式使用 ref
,我们需要为 ref
属性设置回调函数。当我们设置 ref
时, React
会调用这个函数,并将 element
作为第一个参数传递给它。
这是另一个例子的代码。像上面的示例一样,此代码获取 input
标签的文本值,但在这里我们使用回调引用:
// Refs.js class CustomTextInput extends React.Component { constructor(props) { super(props); this.textInput = null; this.setTextInputRef = element => { this.textInput = element; }; } handleSubmit = e => { e.preventDefault(); console.log(this.textInput.value); }; render() { return ( <div> <form onSubmit={e => this.handleSubmit(e)}> <input type="text" ref={this.setTextInputRef} /> <button>Submit</button> </form> </div> ); } }
上面的示例中,我们将 input
标签的 ref
设置为 this.setTextInputRef
。
当组件安装时, React
会将 DOM
元素传递给 ref
的回调;当组件卸载时,则会传递 null
。( ref
回调会在 componentDidMount
和 componentDidUpdate
生命周期之前调用。)
String Ref(已过时)
还有另一种设置 refs
的方法,但它被认为是过时的,可能很快就会被弃用。但是你可能会在其他人的代码中看到它,所以这里说一下。
使用 string refs
,你将会看到这样的 input
标签:
<input type="text" ref="textInput" />
然后,我们可以在组建上得到这样的值: this.refs.textInput.value
- 但是,再次声明,这不应该在新代码中使用,因为这个 API
将被弃用。
转发 Refs (Forwarding Refs)
Ref forwarding
是一种将 ref
通过组件传递给其子节点的技术。它对于可复用组件库和高阶组件(HOC)等情况非常有用。
您可以使用 React.forwardRef
函数将 ref
转发到组件。我们来看下面的例子:
// Ref.js const TextInput = React.forwardRef((props, ref) => ( <input type="text" placeholder="Hello World" ref={ref} /> )); const inputRef = React.createRef(); class CustomTextInput extends React.Component { handleSubmit = e => { e.preventDefault(); console.log(inputRef.current.value); }; render() { return ( <div> <form onSubmit={e => this.handleSubmit(e)}> <TextInput ref={inputRef} /> <button>Submit</button> </form> </div> ); } }
Ref forwarding
允许组件接收一个 ref
,并将它向下传递(换句话说,“转发”它)给子组件。
在上面的示例中,我们使用 input
标签创建了一个名为 TextInput
的组件。那么,我们如何将 ref
传递或转发到 input
标签呢?
首先,我们使用下面的代码创建一个 ref
:
const inputRef = React.createRef();
然后,我们将 ref
通过为组件 <TextInput ref={inputRef}>
指定一个同名的 JSX
的属性,将 ref
向下传递。然后 React
将会把 ref
作为第二个参数转发给 forwardRef
函数。
接下来,我们将此 ref
参数转发给 <input ref={ref}>
。现在可以在外层组件通过 inputRef.current
访问DOM节点的值了。
转发 refs
和高阶组件
最后,让我们看一下使用 refs
的另一个例子,但这次是使用高阶组件(HOC)。
在上面的示例应用程序中,会将所有 input
标签中输入的值在控制台打印。这里已经为 input
标签设置了 ref
属性,接下来,让我们看一下需要如何在高阶组件中传递 / 转发 ref
。
const Input = InputComponent => { const forwardRef = (props, ref) => { const onType = () => console.log(ref.current.value); return <InputComponent forwardedRef={ref} onChange={onType} {...props} />; }; return React.forwardRef(forwardRef); };
这里有一个名为 Input
的高阶组件 ,它接受 InputComponent
作为参数。当用户输入的时候,他还会将 ref
的值在控制台打印。
在 Input
高阶组件内, forwardRef
函数会返回 InputComponent
。 forwardRef
函数中所包含的 ref
参数,是由 React.forwardRef
函数创建的。 高阶组件最终会将包装好的组件作为值返回。
接下来,我们创建一个组件,将 input
作为子组件包含进来。
const TextInput = ({ forwardedRef, children, ...rest }) => ( <div> <input ref={forwardedRef} {...rest} /> {children} </div> );
上面的组件会将 forwardedRef
分配给 ref
属性, 当渲染子组件的时候, input
输入框就会接收到这个 ref
。 ...rest
是 props
的解构(也就是说,我们会将 rest
数组中的所有参数作为 props
传递给 input
组件)。那么我们该如何使用 TextInput
组件呢?像这样:
const InputField = Input(TextInput); class CustomTextInput extends Component { render() { const inputRef = React.createRef(); return <InputField ref={inputRef} />; } }
最后,将 TextInput
传入 Input
高阶组件,会返回一个 InputField component
。
创建一个 ref
,并作为参数传递给 InputField
组件。
结论
与通过 props
和 state
不同, Refs
是一种将数据传递给特定子实例的好方法。
你必须要小心,因为 refs
操纵实际的 DOM
,而不是虚拟的 DOM
,这与 React
思维方式相矛盾。因此,虽然 refs
不应该是通过应用程序流动数据的默认方法,但是当您需要时,它们是可以从 DOM
元素读取数据的好方法。
译注:推荐下「司徒正美」大佬的 React v16.3.0: New lifecycles and context API ,createRef API,forwardRef API 中的示例可以作为补充阅读。
本文已经联系原文作者,并授权翻译,转载请保留原文链接
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- React 函数式组件性能优化指南
- mpvue中小程序自定义导航组件开发指南
- Java 对象属性复制组件:Mapstruct 的项目改造指南
- 为Envoy构建控制面指南第2部分:识别组件
- VS Code 代码编辑器入门指南:核心组件与概念
- 谷歌开源项目风格指南之 Python 风格指南
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。