useRef

作者: skoll | 来源:发表于2020-06-22 19:18 被阅读0次

    使用场景

    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 从不会重新执行
    }
    

    相关文章

      网友评论

        本文标题:useRef

        本文链接:https://www.haomeiwen.com/subject/eotexktx.html