hooks学习笔记5-useRef、useImperativeHandle

55 阅读2分钟

useRef

  • 类组件、React 元素用 React.createRef,函数组件使用 useRef
  • useRef 返回一个可变的 ref 对象,其 current 属性被初始化为传入的参数(initialValue)
  • useRef 返回的 ref 对象在组件的整个生命周期内保持不变,也就是说每次重新渲染函数组件时,返回的ref 对象都是同一个(使用 React.createRef ,每次重新渲染组件都会重新创建 ref)
const refContainer = useRef(initialValue);

使用方法

import React,{useRef} from 'react';
const Demo = ()=>{
    return <div></div>
}
const Father=()=>{
    const inputRef = useRef<any>()
    const Child = React.forwardRef(Demo);
    const getFocus=()=> {
     inputRef.current.focus();
     }
     return (
         <div>
              <input type="text" ref={inputRef} />
             <button onClick={getFocus}>获得焦点</button>
         </div>
     )
 }
export default Father 

forwardRef

因为函数组件没有实例,所以函数组件无法像类组件一样可以接收 ref 属性,因此需要使用forwardRef包裹一层, 子组件接受 props 和 ref 作为参数

function Child(props,ref){
  return (
    <input type="text" ref={ref}/>
  )
}
Child = React.forwardRef(Child);
function Parent(){
  const inputRef = useRef();
  function getFocus(){
    inputRef.current.focus();
  }
  return (
      <>
        <Child ref={inputRef}/>
        <button onClick={getFocus}>获得焦点</button>
      </>
  )
}

useImperativeHandle

  • useImperativeHandle可以让你在使用 ref 时,自定义暴露给父组件的实例值,不能让父组件想干嘛就干嘛

  • 在大多数情况下,应当避免使用 ref 这样的命令式代码。useImperativeHandle 应当与 forwardRef 一起使用

  • 父组件可以使用操作子组件中的多个 ref

function Child(props,parentRef){
    // 子组件内部自己创建 ref 
    let focusRef = useRef();
    let inputRef = useRef();
    useImperativeHandle(parentRef,()=>(
      // 这个函数会返回一个对象
      // 该对象会作为父组件 current 属性的值
      // 通过这种方式,父组件可以使用操作子组件中的多个 ref
        return {
            focusRef,
            inputRef,
            name:'计数器',
            focus(){
                focusRef.current.focus();
            },
            changeText(text){
                inputRef.current.value = text;
            }
        }
    });
    return (
        <>
            <input ref={focusRef}/>
            <input ref={inputRef}/>
        </>
    )

}
Child = forwardRef(Child);
function Parent(){
  const parentRef = useRef();//{current:''}
  function getFocus(){
    parentRef.current.focus();
    // 因为子组件中没有定义这个属性,实现了保护,所以这里的代码无效
    parentRef.current.addNumber(666);
    parentRef.current.changeText('<script>alert(1)</script>');
    console.log(parentRef.current.name);
  }
  return (
      <>
        <ForwardChild ref={parentRef}/>
        <button onClick={getFocus}>获得焦点</button>
      </>
  )
}

useRef补充

useRef 的作用

  • 获取React JSX中的DOM元素,获取后你就可以控制DOM的任何东西了
  • 用useRef来保存变量,注意:useRef 每次都会返回相同的引用,ref不会受到闭包影响,使用useRef来保存的变量永远都是最新的值,它值的改变不会引起Render。

React Hooks 中存在 Capture Value 的特性

function MessageDemo() {
  const [message, setMessage] = useState("");
 
  const showMessage = () => {
    alert("You said: " + message);
  };
 
  const handleSendClick = () => {
    setTimeout(showMessage, 3000);
  };
 
  const handleMessageChange = e => {
    setMessage(e.target.value);
  };
 
  return (
    <>
      <input value={message} onChange={handleMessageChange} />
      <button onClick={handleSendClick}>Send</button>
    </>
  );
}

handleSendClick函数3秒后打印的showMessage是这个函数在执行时的message值,在打印前的这3秒内你再修改message打印也不会更新,而在类组件中不会出现这种情况,只有函数组件有,使用useRef可以跳过Capture Value。

useRef保存变量的用法

  • 保存定时器