在常规的 React 数据六中,props 是父组件与子组件交互的唯一方法。如果需要改子元素,你需要用新的props去重新渲染子元素。 然而,在少数情况下,你需要在常规数据流外强制修改子元素。被修改的子元素可以是React组件实例,或者是一个DOM元素。React团队提供了Refs办法
# 使用Refs 场景
在如下几种情形中,我们可以引入refs:
处理
focus、文本选择或者媒体播放时触发强制动画时
继承第三方DOM库时
注意:如果可以通过声明式实现,就尽量避免使用refs。例如一个场景。如果我们想在Dialog组件中暴露open() 和 close()方法,则传递isOpen状态标志即可。也就是说,我们不应该过度的使用refs,通常较高级别的state更为清晰。
# 在元素本身的DOM元素上添加 Ref
React 支持给任何组件添加特殊的属性。 ref属性接受回调函数,并且当组件 装载(mounted) 或者 卸载(unmounted) 之后,回调函数会立即执行。
当给HTML元素添加 ref 属性时,ref回调接受底层的DOM 元素作为参数。例如,下面的代码使用ref 回调来存储DOM节点的引用
class CustomTextInput extends React.Component {
constructor(props) {
super(props); this.focus = this.focus.bind(this);
}
focus() { // 通过原生的API,显示地聚焦text输入框
this.textInput.focus();
}
render() { // 在实例中通过使用`ref`回调函数来存储text输入框的DOM元素引用(例如:this.textInput)
return (
<div>
<input
type="text"
ref={(input) => { this.textInput = input; }} />
<input
type="button"
value="Focus the text input"
onClick={this.focus} />
</div>
)
}
} React 组件在加载时将DOM元素传入ref 的回调函数,在写在时则会传入 null, 在 componentDidMount 或 componentDidUpdate 这些生命周期回之前执行 ref回调
使用ref 回调只是为了在类上设置一个属性,是访问DOM元素常见模式。 首选的方法是在 ref 回调中设置属性。就像上面的例子一样,他也可以写成ref={input => this.textInput = input}
# 为 类(class)组件添加 Ref
当 ref 属性用于类声明的自定义组件时, ref回到上述收到的参数装载(mounted)的组件实例. 例如如果我们想包装 CustomTextInput 组件,实现组件在 装载(mounted) 后立即点击的效果:
class AutoFocusTextInput extends React.Component {
componentDidMount() { this.textInput.focus();
}
render() { return ( <CustomTextInput
ref={(input) => { this.textInput = input; }} />
);
}
} 需要注意的是,这种方法仅对以类声明的 CustomTextInput 有效,函数式声明的是无效的。
# Refs 与函数式组件
你不能在函数式组件上使用 ref 属性,因为它们没有实例
function MyFunctionalComponent() { return <input />;
}class Parent extends React.Component {
render() { // 这里 *不会* 执行!
return (
<MyFunctionalComponent
ref={(input) => { this.textInput = input; }} />
);
}
} 如果你需要使用 ref ,你需要将组件转化成 类组件,就像需要 生命周期方法 或者 state 一样。然而你可以 在函数式组件内部使用ref 来引用一个 DOM 元素或者 类组件
function CustomTextInput(props) { // textInput必须在这里声明,所以 ref 回调可以引用它
let textInput = null; function handleClick() {
textInput.focus();
} return (
<div>
<input
type="text"
ref={(input) => { textInput = input; }} />
<input
type="button"
value="Focus the text input"
onClick={handleClick}
/>
</div>
);
}
# 对父组件暴露 DOM 节点
在极少数的情况下,你可能希望从父组件中访问子节点的DOM节点。这种情况不被推崇使用,因为他会破坏组件的封装,丢失子组件独立工作的效果。但偶尔也可用于触发焦点或测量子 DOM 节点的大小或位置。
虽然你可以向子组件添加 ref,但这不是一个理想的解决方案,因为你只能获取组件实例而不是 DOM 节点。并且,它还在函数式组件上无效。
相反,在这种情况下,我们建议在子节点上暴露一个特殊的属性。子节点将会获得一个函数属性,并将其作为 ref 属性附加到 DOM 节点。这允许父代通过中间件将 ref回调给子代的 DOM 节点. 这适用于类组件和函数式组件。
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 回调作为一个特殊的inputRef传递给 CustomTextInput,然后 CustomTextInput 通过ref属性将其传递给 <input>。最终,Parent中的 this.inputElement 将被设置为与 CustomTextInput中的 <input> 元素相对应的 DOM 节点。
请注意,上述示例中的 inputRef 属性没有特殊的含义,它只是一般的组件属性。然而,使用<input>本身的ref 属性很重要,因为它告诉 React 将 ref 附加到它的 DOM 节点。
即使 CustomTextInput 是一个函数式组件,它也同样有效。这里在父组件定义的是自定义属性,而不是特殊的 ref 因此不会受影响
这种模式的另一个好处是它能作用很深。假如有个 Parent 组件不需要 DOM 节点 A,但是某个渲染 Parent 的组件(我们称之为Grandparent)需要通过它访问。这时我们可以让 Grandparent 传递 inputRef 给 Parent 组件,然后让Parent组件将其转发给 CustomTextInput
function CustomTextInput(props) { return (
<div>
<input ref={props.inputRef} />
</div>
);
}function Parent(props) { return (
<div>
My input: <CustomTextInput inputRef={props.inputRef} />
</div>
);
}class Grandparent extends React.Component {
render() { return (
<Parent
inputRef={el => this.inputElement = el}
/>
);
}
}总而言之,我们建议尽可能不暴露 DOM 节点,但这是一个有用的解决方式。请注意,此方法要求您向子组件添加一些代码,如果你无法完全控制子组件,最后的办法是使用findDOMNode(),但是不推荐这样做。
作者:果汁凉茶丶
链接:https://www.jianshu.com/p/872e2af252e6
共同學習,寫下你的評論
評論加載中...
作者其他優質文章