Loading... *阅读时间约为 8 分钟。* ***文末有福利哦~*** --- 昨天我们讨论了 React 中的 `useRef` 钩子函数: **{文章链接}** ,其中的一个场景是在函数组件中使用 `useRef` 来创建一个 `ref` 对象,然后将其绑定到一个元素上,从而获取该元素的引用以便对引用的元素做出各种操作。而当我们想要在父组件中访问子组件的引用时该怎么做呢? 答案是通过 React 的 `forwardRef` API 来实现。 ## 初识 forwardRef API `forwardRef` API 允许我们从父组件向子组件传递一个 `ref`,从而让父组件能够访问子组件的 DOM 节点或实例。语法如下: ```jsx const SomeComponent = forwardRef(render); ``` 通常来说,我们可以通过回调函数的方式来让父组件访问到子组件,也就是通过 props 将回调函数传递给子组件,以便在父组件中访问子组件中的 DOM 节点或实例。但在某些情况下,这种方式不够方便或灵活。而 `forwardRef` 则可以提供一种更为灵活的方法来解决这个问题。 当我们使用 `forwardRef` 将子组件包装起来时,我们可以通过 `forwardRef` 的第二个参数 `ref` 访问子组件。然后,将该 `ref` 传递给子组件中需要引用的元素或组件,从而使父组件能够访问子组件的实例或 DOM 节点。这使得 `forwardRef` 成为一种非常强大和灵活的工具,可以帮助我们轻松地访问和操作子组件中的元素或组件。 ## 如何使用 forwardRef API 要使用 `forwardRef` 来访问子组件的 DOM 节点或实例,以下两个步骤缺一不可: 1. 通过 `forwardRef` 包装子组件 在父组件中,将 `ref` 参数传递给通过 `forwardRef` 函数包装子组件这样父组件: ```jsx const ChildComponent = forwardRef((props, ref) => { // 子组件代码 }); ``` `forwardRef` 包裹的是一个函数式组件,函数第二个参数就是父组件传递过来的 `ref` 对象。可以使用我们上一篇文章中探讨的 useRef 方法在父组件中创建一个 `ref` 对象。 2. 在子组件中访问 `ref` 对象 在子组件中,将 `ref` 对象绑定到需要访问的元素或组件上,从而使父组件能够访问该 DOM 元素或组件的实例。 ```jsx const ChildComponent = forwardRef((props, ref) => { return ( <div> {/* ref 对象被绑定在了 input 元素上 */} <input type="text" ref={ref} /> </div> ); }); ``` 我们将 `ref` 对象绑定到 `input` 元素上,这样在父组件中就可以使用 `ref.current` 来访问 `input` 元素的实例。 ```jsx const ParentComponent = () => { const inputRef = useRef(null); const handleClick = () => { inputRef.current.focus(); // 使子组件 input 元素聚焦 }; // 渲染子组件,并将 ref 对象传递给它 return ( <div> <ChildComponent ref={inputRef} /> <button onClick={handleClick}>Focus input</button> </div> ); }; ``` 我们使用 `useRef` 方法来创建一个 `ref` 对象 `inputRef`,并将其传递给子组件 `ChildComponent`。然后,在 `handleClick` 函数中,我们使用 `inputRef.current.focus()` 方法来使 `input` 元素聚焦。 结合 `useRef` 方法,我们通过上面的两个步骤便能方便的访问到子组件中被绑定了 `ref` 对象的 DOM 元素了。 除此之外,我们在编写在高阶组件(Higher-Order Component,HOC)时也会经常用到 `forwardRef` API。高阶组件是一个函数,它接收一个组件作为参数,并返回一个新的组件。`forwardRef` 可以帮助我们确保 `ref` 被正确地传递到被包装的组件中。 ```jsx // 高阶组件 withWrapper 接收一个组件 WrappedComponent 作为参数,并返回一个由 forwardRef 包裹的新组件 function withWrapper(WrappedComponent) { return forwardRef((props, ref) => { return ( <div ref={ref}> <WrappedComponent {...props} /> </div> ); }); } const ChildComponent = ({ text }) => { return <div>{text}</div>; }; const ParentComponent = () => { const wrapperRef = useRef(null); useEffect(() => { console.log(wrapperRef.current); // <div>...</div> }, []); // 将子组件 ChildComponent 作为参数传递给高阶函数 withWrapper,并返回一个新组建 WrappedChildComponent const WrappedChildComponent = withWrapper(ChildComponent); // 渲染由高阶组件返回的新组件,并将 ref 对象 wrapperRef 作为一个属性传递给这个新组建 WrappedChildComponent return ( <div> <WrappedChildComponent ref={wrapperRef} text="Hello, world!" /> </div> ); }; ``` 在上面的示例中,我们定义了一个名为 `withWrapper` 的高阶组件,该函数接收一个组件作为参数,并返回一个新的组件。这个新的组件将被包裹在一个 div 元素中,并将这个 `div` 元素的引用作为 `ref` 传递给被包装的组件。 在 `ParentComponent` 父组件中,我们使用 `withWrapper` 高阶函数来创建一个新的组件 `WrappedChildComponent`,并将其引用传递给 `wrapperRef`。这样,当 `ParentComponent` 渲染时,我们就可以在 `useEffect` 钩子函数中通过 `wrapperRef` 对象访问高阶函数中的 `div` 元素,从而执行一些操作。 ## 应用场景 结合上面的示例我们不难发现,`forwardRef` API 通常适用于以下两个场景: 1. 通过 `ref` 对象访问子组件的 DOM 节点; 2. 在高阶组件中使用 `forwardRef`。 其实还可以在组件库中使用,比如我们熟知的 `antd` 中的 `Modal` 组件就提供了 `ref` 的访问能力。 由此我们知道,通过使用 `forwardRef`,我们可以轻松地将 `ref` 对象传递到子组件中,从而可以访问子组件的实例或者 DOM 节点,执行一些操作。 ## 总结 简单回顾一下本文关于 `forwardRef` API 在 React 中的作用和使用方法: * `forwardRef` 可以用来访问子组件中的 DOM 节点或实例; * 在编写高阶组件时,`forwardRef` 能确保 `ref` 对象被正确的传递到被包装的组件中。 React 中的 `forwardRef` 是一个非常重要的 API,因为它能帮我们处理一些特殊场景,比如文中提到的访问子组件的 DOM 节点或者用于编写高阶组件。通过使用 `forwardRef` API,我们能更加方便的操作 React 组件,还能大大提高我们的开发效率。因此,掌握 React 中的 `forwardRef` 是非常非常必要的。如果不了解这个 API,比如要实现访问子组件 DOM 节点的需求,通过回调函数等方式实现的话将会非常之繁琐。 所以,你学会 `forwardRef` 了吗?学会的小伙伴在下面留言扣个 1 ~ --- 福利来啦~ 关注微信公众号 **码上花甲** 后台回复 **JavaScript权威指南** 可获取这本书的电子版哦,电子书是最新的第 6 版。书名全称为《JavaScript权威指南(原书第6版)》,作者是大名鼎鼎的David Flanagan,书的内容就不用咱多做介绍了吧~ 懂的都懂、前端攻城狮人手一份、 期待你学有所成哦 😘 Last modification:April 11, 2023 © Allow specification reprint Support Appreciate the author AliPayWeChat Like 0 如果觉得我的文章对你有用,请随意赞赏