Refs 提供了一种访问在 render 方法中创建的 DOM 节点或 React 元素的方法。
在典型的 React 数据流中,props是父组件与其子组件交互的唯一方式。要修改一个孩子,你可以用新的道具重新渲染它。但是,在某些情况下,您需要在典型数据流之外强制修改子项。要修改的子元素可以是 React 组件的实例,也可以是 DOM 元素。对于这两种情况,React 都提供了一个逃生口。
refs 有一些很好的用例:
避免将 refs 用于任何可以以声明方式完成的事情。
例如,不是在组件上公开open()和close()方法,而是将prop 传递给它。Dialog``isOpen
open()
close()
Dialog``isOpen
您的第一个倾向可能是在您的应用程序中使用 refs 来“让事情发生”。如果是这种情况,请花点时间更批判地思考在组件层次结构中应该拥有状态的位置。通常,很明显“拥有”该状态的适当位置位于层次结构中的更高级别。有关此示例,请参阅Lifting State Up指南。
笔记 以下示例已更新为使用React.createRef()React 16.3 中引入的 API。如果您使用的是早期版本的 React,我们建议使用回调 refs。
笔记
以下示例已更新为使用React.createRef()React 16.3 中引入的 API。如果您使用的是早期版本的 React,我们建议使用回调 refs。
React.createRef()
Refs 是使用属性创建React.createRef()并附加到 React 元素的。refRefs 通常在构建组件时分配给实例属性,以便可以在整个组件中引用它们。
ref
class MyComponent extends React.Component { constructor(props) { super(props); this.myRef = React.createRef(); } render() { return <div ref={this.myRef} />; } }
当 ref 被传递给 in 中的元素时render,对节点的引用可以在currentref 的属性中访问。
render
current
const node = this.myRef.current;
ref 的值因节点的类型而异:
下面的示例演示了这些差异。
此代码使用 aref来存储对 DOM 节点的引用:
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> ); } }
React 将current在组件挂载时将属性分配给 DOM 元素,并在组件卸载时将其分配回null。ref更新发生在生命周期componentDidMount方法之前componentDidUpdate。
null
componentDidMount
componentDidUpdate
如果我们想包装CustomTextInput上面的内容以模拟它在安装后立即被点击,我们可以使用 ref 来访问自定义输入并focusTextInput手动调用其方法:
CustomTextInput
focusTextInput
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 CustomTextInput extends React.Component { // ... }
默认情况下,您可能不会ref在函数组件上使用该属性,因为它们没有实例:
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} /> ); } }
如果你想让人们把一个ref组件带到你的函数中,你可以使用forwardRef(可能与 结合useImperativeHandle),或者你可以将组件转换为一个类。
forwardRef
useImperativeHandle
但是,只要您引用 DOM 元素或类组件,您就可以在函数组件中**使用该属性:ref**
function CustomTextInput(props) { // textInput must be declared here so the ref can refer to it const textInput = useRef(null); function handleClick() { textInput.current.focus(); } return ( <div> <input type="text" ref={textInput} /> <input type="button" value="Focus the text input" onClick={handleClick} /> </div> ); }
在极少数情况下,您可能希望从父组件访问子组件的 DOM 节点。通常不建议这样做,因为它会破坏组件封装,但它有时对触发焦点或测量子 DOM 节点的大小或位置很有用。
虽然您可以将 ref 添加到子组件,但这不是一个理想的解决方案,因为您只会获得组件实例而不是 DOM 节点。此外,这不适用于功能组件。
如果您使用 React 16.3 或更高版本,我们建议在这些情况下使用ref 转发。Ref 转发允许组件选择将任何子组件的 ref 公开为它们自己的 ref。您可以在 ref 转发文档中找到有关如何将子 DOM 节点公开给父组件的详细示例。
如果您使用 React 16.2 或更低版本,或者如果您需要比 ref 转发提供的更多灵活性,您可以使用这种替代方法并将 ref 作为不同命名的 prop 显式传递。
如果可能,我们建议不要暴露 DOM 节点,但它可能是一个有用的逃生口。请注意,这种方法需要您向子组件添加一些代码。如果您完全无法控制子组件的实现,您的最后一个选择是使用findDOMNode(),但在StrictMode.
findDOMNode()
StrictMode
React 还支持另一种设置 refs 的方法,称为“回调 refs”,它可以更细粒度地控制何时设置和取消设置 refs。
不是传递由ref创建的属性createRef(),而是传递一个函数。该函数接收 React 组件实例或 HTML DOM 元素作为其参数,可以在其他地方存储和访问。
createRef()
下面的示例实现了一个常见模式:使用ref回调将对 DOM 节点的引用存储在实例属性中。
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> ); } }
React 会ref在组件挂载时使用 DOM 元素调用回调,并在组件null卸载时调用它。componentDidMountRefs 保证在或componentDidUpdate触发之前是最新的。
您可以在组件之间传递回调引用,就像使用创建的对象引用一样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将其 ref 回调作为inputRefprop 传递给CustomTextInput,CustomTextInput并将相同的函数作为特殊ref属性传递给<input>. 因此,this.inputElementinParent将被设置为<input>与CustomTextInput.
Parent
inputRef
<input>
this.inputElement
如果您之前使用过 React,您可能会熟悉一个较旧的 API,其中ref属性是字符串,例如"textInput",并且 DOM 节点以this.refs.textInput. 我们建议不要这样做,因为字符串引用存在一些问题,被认为是遗留问题,并且可能会在未来的某个版本中被删除。
"textInput"
this.refs.textInput
笔记 如果您当前this.refs.textInput用于访问 refs,我们建议您使用回调模式或createRefAPI。
如果您当前this.refs.textInput用于访问 refs,我们建议您使用回调模式或createRefAPI。
createRef
如果ref回调被定义为内联函数,它将在更新期间被调用两次,第null一次使用 DOM 元素,然后再次使用 DOM 元素。这是因为每次渲染都会创建一个新的函数实例,所以 React 需要清除旧的 ref 并设置新的。您可以通过将回调定义为类的绑定方法来避免这种ref情况,但请注意,在大多数情况下这无关紧要。
原文链接:https://codingdict.com/