一天练熟一个Hook-useref/useImperativeHandle

77 阅读2分钟

useref

要想使用好ref,得知道ref是什么?ref用来获取组件实例,也就是真实DOM,在class组件中是有实例的,但是函数组件没有实例怎么获取?我们可以使用useref这个hook.

基本使用

import { useRef } from 'react';  
    function MyComponent() {  
    const intervalRef = useRef(0);  
    const inputRef = useRef(null);
   //...

以上是使用useref创建一个ref

import { useRef } from 'react';

export default function Counter() {
  let ref = useRef(0);

  function handleClick() {
    ref.current = ref.current + 1;
    alert('You clicked ' + ref.current + ' times!');
  }

  return (
    <button onClick={handleClick}>
      Click me!
    </button>
  );
}

在这个例子中,创建一个ref并且初始值设置为0;handleClick函数中对ref的值+1;这样做的结果就是当你的组件刷新的时候,ref的值不会被重置为0,这意味着,你可以使用 ref.current 属性来存储和访问在组件多次渲染之间需要保持不变的值。

关于ref转发

将子组件的值转发给父组件,父组件可以调用子组件实例,在react中,使用forwardRef创建支持转发的组件. forwardRef接受一个函数作为参数,该函数返回一个新的函数式组件.

import React, { useRef, forwardRef } from 'react';

const ChildComponent = forwardRef((props, ref) => {
  return (
    <div ref={ref}>
      This is the child component.
    </div>
  );
});

const ParentComponent = () => {
  const childRef = useRef(null);

  const handleButtonClick = () => {
    childRef.current.style.backgroundColor = 'red';
  }

  return (
    <div>
      <button onClick={handleButtonClick}>Change Child Background Color</button>
      <ChildComponent ref={childRef} />
    </div>
  );
};

export default ParentComponent;


在这个例子中在父组件中生成childref,然后ChildComponent中将childref传递ref属性,在ChildComponent组件中forwardRef接受ref,这时候父组件就可以获取子组件实例.而在父组件中,通常不需要写 forwardRef 函数,只需要创建一个 ref 对象,并将其传递给子组件的 ref 属性即可实现 ref 转发。

useImperativeHandle

这个钩子,当你想在父组件中使用子组件实例,但是又不想全部暴露,而是只可以使用子组件中特定的一些函数.

useImperativeHandle 接受两个参数:第一个参数是一个 ref 对象,用于引用子组件实例,第二个参数是一个回调函数,该函数返回一个对象,该对象包含要暴露给父组件的函数。

import React, { forwardRef, useRef, useImperativeHandle } from 'react';

const FancyInput = forwardRef((props, ref) => {
  const inputRef = useRef();

  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    },
    clear: () => {
      inputRef.current.value = '';
    },
  }));

  return <input type="text" ref={inputRef} />;
});

export default function App() {
  const fancyInputRef = useRef();

  function handleClick() {
    fancyInputRef.current.focus();
  }

  function handleClear() {
    fancyInputRef.current.clear();
  }

  return (
    <div>
      <FancyInput ref={fancyInputRef} />
      <button onClick={handleClick}>Focus input</button>
      <button onClick={handleClear}>Clear input</button>
    </div>
  );
}

可以看到首先APP组件将fancyInputRef转发给FancyInput组件, FancyInput组件使用forwardRef来接收ref, 并且在组件里生成inputRef,useImperativeHandle使用这个钩子将父组件传过来的ref和子组件内部的方法连接起来. 就可以在父组件使用focus和clear方法.