zoukankan      html  css  js  c++  java
  • React 函数式组件的 Ref 和子组件访问(useImperativeHandle)

    引入:如何调用函数式组件内部的方法

    对于 React 中需要强制修改子组件的情况,React 提供了 Refs 这种解决办法,使得我们可以操作底层 DOM 元素或者自定的 class 组件实例。除此之外,文档(v17.0.1)对函数式组件另有描述:

    不能在函数式组件上使用ref属性,因为他们没有实例

    在函数式组件和 Hooks 大面积普及的现在,这个特性没有完全对标 class 组件,令人疑惑。不过经过一阵探索和请教,发现确实是有对应的解决方案的:

    useImperativeHandle

    结合 React.forwardRefuseImperativeHandle 文档 应该就能明白是如何使用的。

    简而言之就是可以在函数式组件上使用 ref,通过useImperativeHandle这个hook可以指定暴露给父组件的值和函数。

    案例:

    修改子组件Counter中的值, 达到重置count的目的:

    export default function App() {
      return (
        <div>
          <button>reset</button>
          <Counter />
        </div>
      );
    }
    /** -------------------------------------- */
    function Counter() {
      const [count, setCount] = useState(0);
      function increment() {
        setCount(count + 1);
      }
      return (
        <div>
          <hr />
          <span>{count}</span>
          <button onClick={increment}>+1</button>
        </div>
      );
    }
    

    对于这个案例,将count这个state往上提一层到 App 组件中是比较合适的,但是在这里重点讨论操作子组件

    使用useImperativeHandle,修改代码:

    export default function App() {
      const counterRef = useRef();
      function reset() {
        counterRef.current?.resetCount();
      }
      return (
        <div style={{ padding: 10 }}>
          <button onClick={reset}>reset</button>
          <MyCounter ref={counterRef} />
        </div>
      );
    }
    /** -------------------------------------- */
    function Counter(props, ref) {
      const [count, setCount] = useState(0);
      useImperativeHandle(ref, () => ({
        resetCount: resetCount,
      }));
      function resetCount() {
        setCount(0);
      }
      function increment() {
        setCount(count + 1);
      }
      return (
        <div>
          <hr />
          <span>{count}</span>
          <button onClick={increment}>+1</button>
        </div>
      );
    }
    const MyCounter = React.forwardRef(Counter);
    

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

    到这里,实际上已经达到了和classref对等的效果。通过给函数式组件设置 ref 并调用组件的方法是可行的,useImperativeHandle除了添加方法,也可以指定值暴露出去。

    函数式组件的Ref是什么

    将 ref 设置到 HTML 元素上,获取的是对应的DOM元素,如span:

    设置到 class 组件上,获取的是 class 组件实例:

    设置到函数式组件上的时候,获取的是一个包含可变值或函数的对象,如上例的 Counter 组件:

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

    查看对应的TypeScript类型,React.createRef创建的是React.RefObject类型,是只读的。

    useRef创建的是React.MutableRefObject,是可读写的。可以保存任何可变的值,使用方式类似于class组件的this实例变量。(又是和class组件对标的一个点)

    文档描述 useRef 为可以在其.current属性中保存一个可变值的“盒子”。

    所以实际上应该是,对函数式组件可设置 ref,且设置的 ref 是一个可变对象,存放组件的变量,也能通过useImperativeHandle访问函数式组件的方法。 但是并不能像将 ref 设置到 class 组件和 DOM 元素上那样获取到对应的实例。

  • 相关阅读:
    这次面试就差不多了,你有什么问题需要问我呢?
    C++为啥要使用new
    C#读取“我的文档”等特殊系统路径及环境变量
    C++11 Lambda表达汇总总结
    c#计算 坐标点与坐标点之间的距离
    eclipse svn同步资源库时忽略某些不需要提交文件类型和文件夹
    通俗理解TCP/IP协议三次握手四次分手流程
    mysql 免安装版 启动服务马上关闭
    MySQL数据库安装与配置详解
    word 插入的图片被嵌套在文字底下
  • 原文地址:https://www.cnblogs.com/xuxiaowei/p/14323745.html
Copyright © 2011-2022 走看看