React的Hooks函数useRef和useImperativeHandle

在之前的React的类组件ref使用笔记中,介绍了在类组件中使用ref,本笔记主要记录在函数组件中使用ref,在函数组件中是不能直接使用ref的,而是要使用Hook函数useRef来实现类似在类组件中ref的功能。

初识useRef

useRef 是 React 提供的一个 Hook 函数,它返回的是一个ref对象,返回的ref对象在组件的整个生命周期的中保存的值是不变的。它返回的对象包含一个可变的 current 属性,可以在函数式组件中使用它来保存任何可变值,包括 DOM 元素或组件实例的引用。useRef 的主要作用是保存一些变量,而这些变量的值改变时不会触发组件的重渲染。

useRef 的格式:

const refObj = useRef(initialValue);

initialValue 是ref对象的初始值,可以是任何 JavaScript 类型的值,也可以不传初始值。

useRef 的使用:

console.log(refObj.current)

在返回的ref对象,可以通过.current可以获取保存在useRef的值,当没有传入初始值时,通过.current获取的值为undefined

获取DOM元素

const Parent = () => {
  const refObj = useRef()
  const handleClick =  () =>{
   console.log(refObj.current)
   // 获取input的值
   console.log(refObj.current.value)
  }
  return (
    <div>
      <h2>React的Hooks函数useRef和useImperativeHandlet</h2>
      <input type="text" ref={refObj}/>
      <button onClick={handleClick}>click me</button>
    </div>
  );
};

运行上面代码,就可以获取到input的DOM操作,而useRef除了可以获取DOM外 ,也可以当作一个变量使用,当变量的current值改变时,并不会重新执行函数。

React.creatRef()和useref区别

let prev1,prev2;
const Parent = () => {
  console.log('执行函数了')
  const [count, setCount] = useState(0)
  const refCount1 = useRef(0)
  const refCount2 = React.createRef(0)

  if(!prev1){
    prev1 = refCount1
    prev2 = refCount2
  } else {
    console.log(prev1  === refCount1)// true
    console.log(prev2  === refCount2) // false
  }

  const handleClick =  () =>{
   setCount(count + 1)
  }

  return (
    <div>
      <h2>React的Hooks函数useRef和useImperativeHandlet</h2>
      <p>count:{count}</p>
      <button onClick={handleClick}>click me</button>
    </div>
  );
};

运行上面代码,可以得知:
useRef在每次组件更新的时候,再次执行useRef方法的时候,不会创建新的ref对象,获取到的还是第一次创建的那个ref对象
creatRef在每次组件更新的时候,都会创建一个新的ref对象出来

组件嵌套

通过forwardRef可以将ref转发到子组件,子组件拿到父组件中创建的ref,绑定到自己的某一个元素中,父组件中就可以获取到子组件中绑定了ref的元素。

const Child = forwardRef((props,ref)=>{
  return (
    <div>
      我是子组件
      <input type="text" ref={ref}/>
    </div>
  )
})

const Parent = () => {
  const refDom = useRef()

  const handleClickChild =  () =>{
   console.log(refDom)
   // 获取input的值
   console.log(refDom.current.value)
  }
  return (
    <div>
      <h2>React的Hooks函数useRef和useImperativeHandlet</h2>
      <Child ref={refDom}></Child>
      <button onClick={handleClickChild}>click child</button>
    </div>
  );
};

上面直接使用forwardRef获取子组件的DOM,并不安全,因为这样子组件的DOM直接暴露给了父组件。如果直接暴露给父组件,父组件可以拿到DOM后可以进行任意的操作。上面的代码只是希望父组件可以拿到子组件的value值就行了,其他并不希望它操作,比如:修改元素内容的操作refDom.current.value = "222"等,所以要限制父组件直接操作子组件的DOM。因此要使用useImperativeHandle函数,那它的作用是啥呢?

useImperativeHandle

useImperativeHandle函数有两个参数:

  • 参数一: 传入forwardRef引用父组件的ref对象
  • 参数二: 传入一个回调函数, 返回一个对象,对象里面存储需要暴露给父组件的属性或方法
    通过useImperativeHandle,在父组件中可以使用·xxx.current拿到子组件暴露的属性和方法。
const Child = forwardRef((props,ref)=>{
  const [val, setVal] = useState(0)
  const refInput = useRef()
  const handleClick =  () =>{
    setVal(val + 1)
  }

  const getInput = ()=> {
    return  refInput.current
  }
  // 暴露组件的属性和方法
  useImperativeHandle(ref, ()=>{
    return {
      val,
      handleClick,
      getInput
    }
  })
  return (
    <div>
      我是子组件
      <p>Val:{val}</p>
      <input type="text" ref={refInput}/>
    </div>
  )
})

const Parent = () => {
  const refDom = useRef()

  const handleClickChild =  () =>{
   console.log(refDom)
   // 获取input的值
   console.log(refDom.current.value)
  }
  //改变子组件的val值
  const handleChangeChildVal =  ()=> {
    refDom.current.handleClick()
  }
  //  获取子组件input
  const handleClickGetChild =  () =>{
   console.log(refDom.current.getInput().value)
  }
  return (
    <div>
      <h2>React的Hooks函数useRef和useImperativeHandlet</h2>
      <Child ref={refDom}></Child>
      <button onClick={handleChangeChildVal}>change val</button>
      <button onClick={handleClickChild}>click child</button>
      <button onClick={handleClickGetChild}>child input</button>
    </div>
  );
};

以上如果还想获取子组件内的input的值,就只有重新在子组件创建ref对象,然后暴露出来。这样以来想要在外部使用什么功能完全由内部的子组件决定。


 上一篇
js-draggable实现拖拽元素交换位置 js-draggable实现拖拽元素交换位置
研究了哈拖拽替换dom节点,这里记录下哈,希望对需要的朋友有所帮助! 这里我使用了两种方式尝试过: 1,使用draggable插件 2,直接给要拖拽的元素添加draggable="true" draggable插件 插件
2022-05-07
下一篇 
React的Hooks函数useEffect和useLayoutEffect React的Hooks函数useEffect和useLayoutEffect
React 的 useEffect 是一个重要的 Hook,用于处理组件的副作用,而useLayoutEffect和它一样,都是处理函数组件内的副作用的,下面就来说说这两个Hook函数的用法。 useEffect useEffect是类组件
2022-04-11
  目录