React Refs的使用

Snipaste_20200517_005843.jpg
Refs 提供了一种方式,允许我们访问 DOM 节点或在 render 方法中创建的 React 元素

Refs使用场景:

  • 管理焦点,文本选择或媒体播放。
  • 触发强制动画。
  • 集成第三方 DOM 库。
  1. 访问Dom常用方法

使用 React.createRef() 创建

          
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
class CustomTextInput extends React.Component { constructor(props) { super(props); // 创建一个 ref 来存储 textInput 的 DOM 元素 this.textInput = React.createRef(); this.focusTextInput = this.focusTextInput.bind(this); } focusTextInput() { // 直接使用原生 API 使 text 输入框获得焦点 // 注意:我们通过 "current" 来访问 DOM 节点 this.textInput.current.focus(); } render() { // 告诉 React 我们想把 <input> ref 关联到 // 构造器里创建的 `textInput` 上 return ( <div> <input type="text" ref={this.textInput} /> <input type="button" value="Focus the text input" onClick={this.focusTextInput} /> </div> ); } } export default CustomTextInput

refs的另一种方法 回调refs

          
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
class CustomTextInput extends React.Component { constructor(props) { super(props); this.textInput = null; this.setTextInputRef = element => { this.textInput = element; }; this.focusTextInput = () => { // 使用原生 DOM API 使 text 输入框获得焦点 if (this.textInput) this.textInput.focus(); }; } componentDidMount() { // 组件挂载后,让文本框自动获得焦点 this.focusTextInput(); } render() { // 使用 `ref` 的回调函数将 text 输入框 DOM 节点的引用存储到 React // 实例上(比如 this.textInput) return ( <div> <input type="text" ref={this.setTextInputRef} /> <input type="button" value="Focus the text input" onClick={this.focusTextInput} /> </div> ); } } export default CustomTextInput

利用回调ref来控制获取子组件dom

          
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
function CustomTextInput(props) { return ( <div> <input ref={props.inputRef} /> </div> ); } class Parent extends React.Component { inputElement=null componentDidMount(){ this.inputElement.value="我用ref控制下CustomTextInput" } render() { return ( <CustomTextInput inputRef={el =>{console.log(el); this.inputElement = el}} /> ); } } export default Parent

这个相当于把inputRef这个函数作为属性传给CustomTextInput,this还是绑定在父组件,从而间接获取input的ref

默认情况下,你不能在函数组件上使用 ref 属性,因为它们没有实例: 如果要在函数组件中使用 ref,你可以使用 forwardRef(可与 useImperativeHandle 结合使用),或者可以将该组件转化为 class 组件。

refs转发

          
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
const FancyButton = React.forwardRef((props, ref) => ( <button ref={ref} className="FancyButton" {...props}> {props.children} </button> )); class Parent extends React.Component { fancyRef = React.createRef(); fancyClick = () => { console.log("我拿到的是button元素", this.fancyRef.current) } render() { return ( <div> <FancyButton ref={this.fancyRef} onClick={this.fancyClick} >Fancy Button </FancyButton> </div> ) } } export default Parent

官方解释:

  1. 我们通过调用 React.createRef 创建了一个 React ref 并将其赋值给 ref 变量。
  2. 我们通过指定 ref 为 JSX 属性,将其向下传递给
  3. React 传递 ref 给 forwardRef 内函数 (props, ref) => ...,作为其第二个参数。
  4. 我们向下转发该 ref 参数到
  5. 当 ref 挂载完成,ref.current 将指向

注意

第二个参数 ref 只在使用 React.forwardRef 定义组件时存在。常规函数和 class 组件不接收 ref 参数,且 props 中也不存在 ref。

Ref 转发不仅限于 DOM 组件,你也可以转发 refs 到 class 组件实例中。

下面是高阶组件使用Ref转发

          
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
//这是一个包装函数 能打印出输入的值 const Decorate = InputComponent => { const forwardRef = (props, ref) => { const print = () => console.log("ref当前的值", ref.current.value); return <InputComponent forwardedRef={ref} onChange={print} {...props} />; }; return React.forwardRef(forwardRef); }; //包装函数将转发ref属性传给需要包装的组件 const TextInput = ({ forwardedRef, children, ...rest }) => ( <div> <input ref={forwardedRef} {...rest} /> {children} </div>); const InputField = Decorate(TextInput); class CustomTextInput extends React.Component { render() { const inputRef = React.createRef(); return <InputField ref={inputRef}>children</InputField >; } } export default CustomTextInput

最后 勿过度使用Refs 多考虑状态提升,在 React 中,将多个组件中需要共享的 state 向上移动到它们的最近共同父组件中,便可实现共享 state。这就是所谓的“状态提升”。

总之,ref在实际中还是用的不是很多,特别是转发ref的使用,主要借此来记录ref的使用

本文主要参考 官方文档

(完)
React生命周期
一张图,一段代码
七宗罪
色欲,暴食,贪婪,懒惰,愤怒,嫉妒,傲慢,你有几宗罪?
React setState
setState进阶理解
爆火的“闻神”
“闻神”,“闻会军”,“利安闻会军驾驶员服务部”
vue3.2 Keepalive踩坑
vite-plugin-vue-setup-extend 插件给script标签赋值name属性,小问题花费我一天找答案
初读《穷查理宝典》
巴菲特合伙人查理芒格传记
等待你的评论