useRef

使用场景

1 .获取子组件的实例,只有类组件可以使用
2 .在函数组件中定义一个全局变量,不会因为重复render重复声明,类似于组件的this.xxx

获取子组件实例

function Example(){
    const inputEl=useRef(null)
    
    function handleClick(){
        console.log(inputEl)
        inputEl.current.focus()
    }

    return (
        <>
        <input type="text" ref={inputEl}/>
        <button onClick={handleClick}>点击</button>
        </>
    )
}

类组件属性

1 .我们需要保证函数组件每次render之后,某些变量不会被重复声明,比如说Dom节点,定时器id等
2 .在类组件中,可以通过给类添加一个自定义属性来保留,比如this.xx
3 .虽然可以通过useState来保留变量的值,但是useState会触发组件render,这里完全是不需要的,需要使用useRef来实现

function App () {
    const [ count, setCount ] = useState(0)
    const timer = useRef(null)
    let timer2 
    
    useEffect(() => {
      let id = setInterval(() => {
        setCount(count => count + 1)
        console.log(timer2)
      }, 500)
  
      console.log('123‘)
    // 确实setInterval在里面跑逻辑,外面的没有改
      timer.current = id
      timer2 = id
      return () => {
        clearInterval(timer.current)
      }
    }, [])
  
    const onClickRef = useCallback(() => {
      clearInterval(timer.current)
    }, [])
  
    const onClick = useCallback(() => {
      clearInterval(timer2)
    }, [])
  
    return (
      <div>
        点击次数: { count }
        <button onClick={onClick}>普通</button>
        <button onClick={onClickRef}>useRef</button>
      </div>
      )
  }
export default App
//看起来不用ref也是可以实现的,难道这个bug已经修了吗??

4 .app组件每次render,都会重新声明一次timer2,好像不是吧,打印出来tmer2一直都是最初是的id..

文档

1 .useRef返回一个可变的ref对象,其.current属性被初始化为传入参数initialValue
2 .返回的ref兑现挂载组件的整个生命周期都保持不变
3 .useRef可以保存任何类型的值,类似于在class中使用this.xxx
4 .他创建的是一个普通的js对象,而useRef和自己创建一个对象的区别是,useRef会在每次渲染时返回对同一个ref对象
5 .当ref内容发生变化时,useRef是不会通知你的,变更.current属性也不会引发组件的重新渲染

forwardRef

1 .函数组件没有实例,所以函数组件无法像类组件一样接收ref属性
2 .forwardRef 可以在父组件中操作子组件的ref对象
3 .forwardRef可以将父组件中的ref对象转发到子组件的dom上
4 .子组件接收props,ref,作为参数

function Child(props,ref){
//子组件这里要把props和ref分开作为参数传进来
  return (
    <input type="text" ref={ref}/>
  )
}
Child = React.forwardRef(Child);
//其实就是在之前的组件外面包一层,父组件使用时没有任何区别的
function Parent(){
  let [number,setNumber] = useState(0); 
  // 在使用类组件的时候,创建 ref 返回一个对象,该对象的 current 属性值为空
  // 只有当它被赋给某个元素的 ref 属性时,才会有值
  // 所以父组件(类组件)创建一个 ref 对象,然后传递给子组件(类组件),子组件内部有元素使用了
  // 那么父组件就可以操作子组件中的某个元素
  // 但是函数组件无法接收 ref 属性 <Child ref={xxx} /> 这样是不行的
  // 所以就需要用到 forwardRef 进行转发
  const inputRef = useRef();//{current:''}
  function getFocus(){
    inputRef.current.value = 'focus';
    inputRef.current.focus();
  }
  return (
      <>
        <Child ref={inputRef}/>
        <button onClick={()=>setNumber({number:number+1})}>+</button>
        <button onClick={getFocus}>获得焦点</button>
      </>
  )
}

实现类似this的效果,可以在函数内访问到最新的数据

1 .可以简单的跳出

function Example(props) {
  // 把最新的 props 保存在一个 ref 中
  const latestProps = useRef(props);
  useEffect(() => {
    latestProps.current = props;
  });

  useEffect(() => {
    function tick() {
      // 在任何时候读取最新的 props
      console.log(latestProps.current);
    }

    const id = setInterval(tick, 1000);
    return () => clearInterval(id);
  }, []); // 这个 effect 从不会重新执行
}

refs补充

1 .我们利用render方法得到了一个App组件的实例,然后就可以对他做一些操作,但是在组件内,jsx是不会返回一个组件的实例的,只是一个ReactElement,只是告诉React被挂在的组件应该长什么样子,
2 .ref的意思就是组件被调用的时候会新建一个改组件的实例,然后ref指向这个实例
3 .可以通过ref获得DOM节点.人如果把refs放到React组件,就会获得组件的实例.这样可以调用组件的实例方法
4 .组件的ref可以通过findDomNode来返回生成的DOM


image.png

5 .为了防止内存泄露,所有组件卸载的时候都会把ref置为null

useRef在函数组件中的使用

1 .之前class的写法是可以随时绑定在class上,但是function的操作是不能简单绑定上去的.需要操作

app.js里面
const contentRef=useRef(null)
<Content ref={contentRef}>

content.js
import React,{useImperativeHandle} from 'react'
function Content(props,ref){
    function resetCount(){
      //做一些事情
    }

    useImperativeHandle(ref,()=>{
        resetCount,resetCount,
//一个是传递到外面的函数名,一个是里面的函数u
    })
}
export default React.ForwardRef(Count)

2 .useImperativeHandle中定义了resetCount,以及使用React.forward获取ref,在app中为content中定义ref属性,然后就可以在外部父组件中使用通过ref调用子组件的resetCount方法

3 .函数式组件的Ref是什么?就是里面暴露出来的东西
4 .绑定到HTML元素上,对应的DOM元素


image.png

5 .绑定在class组件,获取的class组件实例


image.png

React.createRef,useRef

1 .两者都是创建了一个包含current属性的对象,绑定ref时,对应的属性和函数都在current对应的对象中

2 .createRef创建的时React.RefObject类型.只能读

3 .useRef创建的是React.MutableRefObject,是可读写的.可以保存任何可变的值,使用方式类似于class组件的this实例变量
4 .对函数式组件可设置ref,且设置的ref是一个可变对象,存放组件的变量,也能通过useImperativeHandle访问函数式组件的方法.但是不能像ref设置到class组件和DOM元素上那样可以获取到实例

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,372评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,368评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,415评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,157评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,171评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,125评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,028评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,887评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,310评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,533评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,690评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,411评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,004评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,812评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,693评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,577评论 2 353