React ref、forwardRef 和useRef的学习

关于ref

组件内使用ref,获取dom元素

class Test extends Component {
  constructor(props) {
    super(props);
    this.state = { 
      myDiv:createRef()
     }
  }

  componentDidMount(){
    console.log("this.myDiv.current", this.state.myDiv.current);
    // 打印结果: this.myDiv.current <div>​ref获取的dom元素​</div>​
  }

  render() { 
    return ( 
      <div ref={this.state.myDiv}>ref获取的dom元素</div>
     );
  }
}
 
export default Test;

ref作为子组件的属性,获取的是该子组件

import React, { Component, createRef } from 'react';

class Test extends Component {
  constructor(props) {
    super(props);
    this.state = { 
      myDiv:createRef()
     }
  }

  componentDidMount(){
    console.log("this.myDiv.current", this.state.myDiv.current);
    // 打印的是Child这个子组件(包括属性:props等),而不是Child里面的div
  }

  render() { 
    return ( 
      <Child ref={this.state.myDiv}/>
     );
  }
}

class Child extends Component {
  constructor(props) {
    super(props);
    this.state = { }
  }
  render() { 
    return (  
      <div>我是子组件</div>
    );
  }
}

export default Test;

如果想获得Child里面的div,应该如下修改:

// 父组件
<Child myRef={this.state.myDiv}/>
   
// 子组件
 <div ref={this.props.myRef}>我是子组件</div>

关于forwardRef

简单使用forwardRef

const Child = forwardRef((props, ref)=>{
  return (
  	<div ref={ref}>{props.txt}</div>
  )
})

// 修改父组件
<Child ref={this.state.myDiv} txt="parent props txt"/>

ref属性不能用在函数组件上,因为函数组件没有实例。

forwardRef在高阶组件中的使用

引用传递(Ref forwading)是一种通过组件向子组件自动传递 引用ref 的技术。对于应用者的大多数组件来说没什么作用。但是对于有些重复使用的组件,可能有用。例如某些input组件,需要控制其focus,本来是可以使用ref来控制,但是因为该input已被包裹在组件中,这时就需要使用Ref forward来透过组件获得该input的引用。

作者:pipu
链接:https://www.jianshu.com/p/fac884647720
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

之前在 @Dell Lee 的一篇文章中看到说没有办法传递ref到一个functional stateless component,当时十分naive的项目中实验了一下,以为能够传递ref,结果发现是因为我的疏忽,这个组件上面还有一层HOC,而这个HOC不是functional stateless component我误以为能够传递ref。不过也在探讨过程中发现只要名字不是ref的话其实就可以通过props进行传递。

不过这一切都将成为历史,伟大的fb在react 16.3中新加了一个React.forwardRef函数使这一切变得非常符合我的口味!

import React, { Component, createRef, forwardRef } from 'react';

const bindRef = (WrapperComponent)=>{
  const ConvertRef = (props)=>{
    const { forwardRef, ...other } = props;
    return <WrapperComponent {...other} ref={forwardRef}/>
  }

  return forwardRef((props, ref)=>{
    return (
      <ConvertRef {...props} forwardRef={ref} />
    )
  })
}

const Child = forwardRef((props, ref)=>{
  return (
    <div ref={ref}>{props.txt}</div>
  )
})

const Wrappr = bindRef(Child);

class Test extends Component {
  constructor(props) {
    super(props);
    this.myDiv = createRef();
  }

  componentDidMount(){
    console.log("this.myDiv.current", this.myDiv.current);
  }

  render() { 
    return ( 
      <Wrappr ref={this.myDiv} txt="parent props txt"/>
     );
  }
}

 
export default Test;

关于useRef

比较createRefuseRef

在第一个例子中,我们使用了createRef,为了对比useRef(因为useRef只能在函数组件中使用),所以我们接下来我们把它改成函数组件:

import React, {createRef, useEffect } from 'react';

const Test = ()=>{
  const myDiv = createRef();

  useEffect(()=>{
    console.log("this.myDiv.current", myDiv.current);
  })

  return(
    <div ref={myDiv}>ref获取的dom元素</div>
  )

}
 
export default Test;

我们用useRef替换createRef:

const myDiv = useRef();

效果是一样的。所以为什么会用useRef?

createRef 与 useRef 的区别

useRef returns a mutable ref object whose .current property is initialized to the passed argument (initialValue). The returned object will persist for the full lifetime of the component.

换句人话说 , useRef 在 react hook 中的作用, 正如官网说的, 它像一个变量, 类似于 this , 它就像一个盒子, 你可以存放任何东西.

*createRef 每次渲染都会返回一个新的引用,而 useRef 每次都会返回相同的引用。*

import React, {useState, useRef, createRef, useEffect } from 'react';

const Test = ()=>{
  const [ index, setIndex ] = useState(1);
  const createRefDiv = createRef();
  const useRefDiv = useRef();

  if(!createRefDiv.current){
    createRefDiv.current = index;
  }

  if(!useRefDiv.current){
    useRefDiv.current = index;
  }

  return(
    <>
      <div>createRefDiv.current: {createRefDiv.current}</div>
      <div>useRefDiv.current: {useRefDiv.current}</div>
      <div>
        <button onClick={()=>setIndex(index+1)}>改变Index</button>
      </div>
    </>
  )

}
 
export default Test;

初始化时createRefDiv.currentuseRefDiv.current都等于index,当点击按钮的时候,createRefDiv.current的值发生了改变,但是useRefDiv.current没有。

实际场景

import React, {useState, useRef, createRef, useEffect } from 'react';

const Test = ()=>{
  const [ index, setIndex ] = useState(1);

  const handleAlertClick = ()=>{
   setTimeout(()=>{
    alert("You clicked on: " + index );
   },3000);
  }

  return(
    <>
      <div>You clicked { index } times.</div>
      <button onClick={()=>setIndex(index+1)}>改变Index</button>
      <button onClick={handleAlertClick}>弹出Index</button>
    </>
  )

}
 
export default Test;

当点击改变index的按钮一次之后,点击弹出Index,之后再多次点击改变index,最后弹出来的index的值时2。

import React, {useState, useRef, createRef, useEffect } from 'react';

const Test = ()=>{
  const [ index, setIndex ] = useState(1);
  const lastestIndex = useRef();

  useEffect(()=>{
    lastestIndex.current = index;
  })

  const handleAlertClick = ()=>{
   setTimeout(()=>{
    alert("You clicked on: " + lastestIndex.current );
   },3000);
  }

  return(
    <>
      <div>You clicked { index } times.</div>
      <button onClick={()=>setIndex(index+1)}>改变Index</button>
      <button onClick={handleAlertClick}>改变Index</button>
    </>
  )

}
 
export default Test;

因为 useRef 每次都会返回同一个引用, 所以在 useEffect 中修改的时候 ,在 alert 中也会同时被修改. 这样子, 点击的时候就可以弹出实时的 index了

获取上一次的值

import React, {useState, useRef, createRef, useEffect } from 'react';

const Test = ()=>{
  const [ index, setIndex ] = useState(1);
  const lastestIndex = useRef();

  useEffect(()=>{
    lastestIndex.current = index;
  })

  return(
    <>
      <div>You clicked { index } times.</div>
      <div>lastestIndex.current: { lastestIndex.current }.</div>
      <button onClick={()=>setIndex(index+1)}>改变Index</button>
    </>
  )

}
 
export default Test;

改变为自定义hook

import React, {useState, useRef, createRef, useEffect } from 'react';

const useLastestIndex = state =>{
  const ref = useRef();
  useEffect(()=>{
    ref.current = state;
  })
  return ref.current;
}

const Test = ()=>{
  const [ index, setIndex ] = useState(1);
  const lastestIndex = useLastestIndex(index);


  return(
    <>
      <div>You clicked { index } times.</div>
      <div>lastestIndex: { lastestIndex }.</div>
      <button onClick={()=>setIndex(index+1)}>改变Index</button>
    </>
  )

}
 
export default Test;

总结

  • useRef 就像是可以在其 .current 属性中保存一个可变值的“盒子”;
  • ref 对象以 <div ref={myRef} />形式传入组件,则无论该节点如何改变,React 都会将 ref 对象的 .current 属性设置为相应的 DOM 节点,但是useRef()ref` 属性更有用。它可以很方便地保存任何可变值;
  • useRef 会在每次渲染时返回同一个 ref 对象;
  • 当 ref 对象内容发生变化时,useRef不会通知你。变更 .current 属性不会引发组件重新渲染。

参考:

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值