在使用React时,我们默认的思维方式应该不是强制修改DOM,而是通过传入props来重新渲染组件。然而,有些情况下修改DOM是不可避免的。React中的Refs提供了一种访问在render()方法中创建的React元素(或DOM节点)的方法。当父组件需要与子组件进行交互时,我们通常会使用props来传递相关信息。然而,在某些情况下,我们可能需要修改一个孩子而不用新的道具重新渲染它。这时候需要refs出现。我应该什么时候使用Refs?我们建议在以下情况下使用refs:与第三方DOM库集成触发命令式动画管理焦点、文本选择或媒体播放译注:第三点是否也可以理解为使用事件对象?在React中,它是一个合成事件(SyntheticEvent)。官方文档提到:Avoiduserefsforanythingthatcanbedonewithadeclarativeimplementation.所以一旦我们确定我们真的应该使用refs,我们需要如何使用它们呢?在React中使用Refs你可以通过多种方式使用refs:React.createRef()回调refs(回调refs)Stringrefs(过时)转发refs(转发refs)接下来,让我们看看每个实现:React.createRef()Refs可以使用React.createRef()函数创建并通过ref属性附加到React组件中的HTML元素。通常,ref在组件的构造函数中创建,使其在整个组件中可用。例如:[JavaScript]PlainTextViewCopyCode?classMyComponentextendsReact.Component{constructor(props){super(props);this.firstRef=React.createRef();}render(){return;}}如上所示:在构造函数中创建了一个ref实例,并赋值给this.firstRef,在render()方法里面,将构造函数中创建的ref传递给了div接下来,我们看一个在React组件中使用refs的例子。使用Refs聚焦输入这是另一个例子:[JavaScript]PlainTextViewCopyCode?//Ref.jsclassCustomTextInputextendsReact.Component{constructor(props){super(props);//创建一个ref来存储textInputDOM元素this.textInput=React.createRef();this.focusTextInput=this.focusTextInput.bind(this);}focusTextInput(){//使用原始DOMAPI显式聚焦文本输入//注意:我们正在访问“current”以获取DOM我们在构造函数中创建了return(
);}}在上面的代码块中,我们构建了一个按钮,当单击该按钮时,它会自动将页面聚焦在输入框上。首先,我们在构造函数中创建一个ref实例,赋值给this.textInput,然后通过ref属性赋值给input元素。[HTML]Plaintextviewcopycode?1
注意当ref属性被HTML元素使用时(比如当前例子中的input元素),在构造函数中,使用React.createRef()创建的ref将从底层DOM元素接收当前值。译注:这里的current应该是一个合成事件(SyntheticEvent),意思是访问DOM值,我们需要这样写:【JavaScript】纯文本视图复制代码?1this.textInput.current;第二个元素是一个按钮,点击它会自动聚焦到第一个输入框。我们为onClick属性设置了this.focusTextInput函数。[JavaScript]纯文本视图复制代码?
函数focusTextInput()使用JavaScript的标准函数构建DOM。.focus()方法会将光标聚焦在文本输入框上。[JavaScript]PlainTextViewCopyCode?focusTextInput(){this.textInput.current.focus();}最后,focusTextInput函数在构造方法中绑定如下:[JavaScript]PlainTextViewCopyCode?this.focusTextInput=this.focusTextInput.bind(this);从ref获取值在这个例子中,我们将看到如何为input输入框设置ref属性,并通过ref获取值。一个例子如下:在这个例子中,我们创建一个input输入框来输入一个值。然后,当提交按钮被点击时,我们将读取这个值并将它打印到控制台。[JavaScript]纯文本视图复制代码?//Ref.jsclassCustomTextInputextendsReact.Component{constructor(props){super(props);//创建一个ref来存储textInputDOMelementthis.textInput=React.createRef();}handleSubmit=e=>{e.preventDefault();console.log(this.textInput.current.value);};render(){//告诉React我们想将
ref//与我们在构造函数中创建的textInput返回(
this.handleSubmit(e)}>
);}}同样,我们使用React.createRef()函数创建一个ref实例,然后将其分配给实例变量this.textInput。在render函数中,我们要读取表单下输入框的值。我们如何读取这个值?通过为输入分配一个ref,然后读取ref的值。[JavaScript]纯文本视图复制代码?1
点击提交按钮,上例中的表单元素会通过onSubmit方法调用this.handleSubmit函数,并在控制台显示打印输入框中的信息。[JavaScript]纯文本视图复制代码?handleSubmit=e=>{e.preventDefault();console.log(this.textInput);};上面,参数e包含事件对象。我们使用e.preventDefault()来告诉浏览器我们正在处理被点击的提交按钮并且我们不希望此事件“冒泡”(意思是阻止浏览器的默认行为)。译注:这里可以看下React对事件的处理:React的另一个区别是不能通过返回false来阻止默认行为。你必须明确地使用preventDefault在上面的例子中,我们打印了this.textInput并且我们可以在控制台中看到一个ref对象。[JavaScript]纯文本视图复制代码?1Object{current:HTMLInputElement}请注意,它有一个current属性,即HTMLInputElement。这是输入DOM元素本身,而不是实际值。我们必须使用this.textInput.current.value来获取输入标签的实际值:[JavaScript]PlainTextViewCopyCode?handleSubmit=e=>{e.preventDefault();console.log(this.textInput.current.value);};使用refs是一种直接从表单中提取值的方法:只需将ref设置为输入标签,在需要时提取值即可。Refs回调Refs回调是在React中使用refs的另一种方式。要以这种方式使用ref,我们需要为ref属性设置一个回调函数。当我们设置ref时,React将调用此函数,将元素作为第一个参数传递给它。这是另一个示例的代码。和上面的例子一样,这段代码获取了输入标签的文本值,但是这里我们使用了一个回调引用:[JavaScript]plaintextViewCopyCode?//Refs.jsclassCustomTextInputextendsReact.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(
this.handleSubmit(e)}>
);}}在上面的例子中,我们将input标签的ref设置为this.setTextInputRef。React会在组件挂载时将DOM元素传递给ref回调,在组件卸载时传递null。(在componentDidMount和componentDidUpdate生命周期之前调用ref回调。)StringRef(过时)还有另一种设置refs的方法,但它被认为已过时并且可能很快就会被弃用。但是你可能会在其他人的代码中看到它,所以在这里。使用stringrefs,你会看到这样一个输入标签:[HTML]plaintextViewcopycode?
然后,我们可以在组件上获取这个值:this.refs.textInput.value-但是,同样,这不应在新代码中使用,因为此API将被弃用。转发引用(ForwardingRefs)Ref转发是一种通过组件将ref传递给其子组件的技术。它对于可重用组件库和高阶组件(HOC)之类的东西很有用。您可以使用React.forwardRef函数将ref转发到组件。让我们看看下面的例子:[JavaScript]PlainTextViewCopyCode?//Ref.jsconstTextInput=React.forwardRef((props,ref)=>(
));constinputRef=React.createRef();classCustomTextInputextendsReact.Component{handleSubmit=e=>{e.preventDefault();console.log(inputRef.current.value);};render(){return(
this.handleSubmit(e)}>
);}}引用转发允许组件接收引用并将其向下传递(换句话说,“转发”它)给子组件。在上面的示例中,我们使用input标签创建了一个名为TextInput的组件。那么,我们如何将ref传递或转发给input标签呢?首先,我们使用以下代码创建一个ref:[JavaScript]plaintextViewCopyCode?1constinputRef=React.createRef();然后,我们通过指定具有相同名称属性的JSX将ref传递给组件
,将ref向下传递。React然后会将ref作为第二个参数转发给forwardRef函数。接下来,我们将此ref参数转发给。现在你可以在外部组件中通过inputRef.current访问DOM节点的值。转发refs和高阶组件最后,让我们看一下使用refs的另一个示例,但这次使用高阶组件(HOC)。在上面的示例应用程序中,输入标签中输入的所有值都会打印到控制台。这里已经为input标签设置了ref属性,接下来我们看看在高阶组件中需要如何传递/转发ref。[JavaScript]纯文本视图复制代码?constInput=InputComponent=>{constforwardRef=(props,ref)=>{constonType=()=>console.log(ref.current.value);return;};returnReact.forwardRef(forwardRef);};有一个名为Input的高阶组件,它接受InputComponent作为参数。它还会在用户键入时将ref的值打印到控制台。在InputHOC内部,forwardRef函数返回InputComponent。forwardRef函数中包含的ref参数是由React.forwardRef函数创建的。高阶组件最终会将包装的组件作为值返回。接下来,我们创建一个包含输入作为子组件的组件。[JavaScript]纯文本视图复制代码?constTextInput=({forwardedRef,children,...rest})=>({children}分区>);上面的组件会将forwardedRef赋值给ref属性,子组件渲染时,输入框会收到这个ref。...rest是对props的解构(即我们将rest数组中的所有参数作为props传递给input组件)。那么我们如何使用TextInput组件呢?像这样:[JavaScript]PlainTextViewCopyCode?constInputField=Input(TextInput);classCustomTextInputextendsComponent{render(){constinputRef=React.createRef();return;}最后将TextInput传入Input高阶组件,返回一个InputField组件。创建一个ref并将其作为参数传递给InputField组件。结论与通过props和state不同,Refs是将数据传递给特定子实例的好方法。你必须小心,因为refs操纵实际的DOM,而不是虚拟的,这与React的思维方式相矛盾。因此,虽然refs不应该是通过应用程序传输数据的默认方式,但它们是在需要时从DOM元素读取数据的好方法。