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 元素上那样获取到对应的实例。

  • 相关阅读:
    几个新角色:数据科学家、数据分析师、数据(算法)工程师
    人类投资经理再也无法击败电脑的时代终将到来了...
    Action Results in Web API 2
    Multiple actions were found that match the request in Web Api
    Routing in ASP.NET Web API
    how to create an asp.net web api project in visual studio 2017
    网站漏洞扫描工具
    How does asp.net web api work?
    asp.net web api history and how does it work?
    What is the difference between a web API and a web service?
  • 原文地址:https://www.cnblogs.com/xuxiaowei/p/14323745.html
Copyright © 2011-2022 走看看