Forwarding Refs
在react
中我们最开始接触到ref
是在表单组件中的非受控组件,我们先来回顾一下这个
// 类 组件
class Hello extends React.Component {
// 1、调用 React.createRef() 方法创建一个 ref 对象
txtRef = React.createRef()
handleClick = () => {
// 3、通过 ref 对象获取到文本框的值
console.log(this.txtRef.current.value)
}
render() {
return (
<div>
{/* 2、将创建好的 ref 对象添加到文本框中 */}
<input type="text" ref={this.txtRef} />
<button onClick={this.handleClick}>click</button>
</div>
)
}
}
这里我们在组件Hello
中创建了一个ref
并且把这个ref
传递给了一个input
,这样我们就能获取到input
中的value
,这里我们对 ref 只进行了一次的传递,我们可以对将 ref 进行一层层的向下传递
接下来我们以官网的例子来看看
const FancyButton = React.forwardRef((props, ref) => (
<button ref={ref} className="FancyButton">
{props.children}
</button>
))
// You can now get a ref directly to the DOM button:
const ref = React.createRef()
<FancyButton ref={ref}>Click me!</FancyButton>
同样的先创建一个 ref
然后把这个 ref
作为组件的一个属性进行传递,在该组件中通过 React.forwardRef
这个方法来进行接收,这样我们就能在 React.forwardRef
内部获取到传递的 ref ,并且可以再次把这个 ref 传递个一个 react 元素或组件,最后的结果就是我们用React.createRef
创建的ref
现在指向的就是button
这个元素了,接下来想要获取这个元素就和之前的方式一样了,使用ref.current
获取的就是对应的元素。
注意:只有在
React.forwardRef
中才能通过第二个参数获取到对应的 ref,在函数组件和类组件中是无法通过props
获取到的。
React.forwardRef
接受一个函数作为参数,这个函数接收两个参数,一个参数是 props
另一个就是 ref
,然后会返回一个 react 节点
const reactNode = React.forwardRef((props, ref) => reactNode)
ref 在高阶组件中的使用
function logProps(WrappedComponent) {
class LogProps extends React.Component {
componentDidUpdate(prevProps) {
console.log("old props:", prevProps)
console.log("new props:", this.props)
}
render() {
return <WrappedComponent {...this.props} />
}
}
return LogProps
}
import FancyButton from "./FancyButton"
const ref = React.createRef()
// 我们导入的 FancyButton 组件是高阶组件(HOC)LogProps。
// 尽管渲染结果将是一样的,
// 但我们的 ref 将指向 LogProps 而不是内部的 FancyButton 组件!
// 这意味着我们不能调用例如 ref.current.focus() 这样的方法
<FancyButton label="Click Me" handleClick={handleClick} ref={ref} />
上面也说了在函数组件和类组件中是无法获取到传递的 ref
的,所以这也就导致了现在的 ref 指向的就是当前的组件,而不是高阶组件中包裹的那个组件,要解决这个问题也是简单的,这就需要我们在高阶组件内部再进行一个处理,就是通过使用上面的 React.forwardRef
来对高阶组件进行一次包裹,最终我们使用的组件其实就是 React.forwardRef
返回的。
function logProps(Component) {
class LogProps extends React.Component {
componentDidUpdate(prevProps) {
console.log("old props:", prevProps)
console.log("new props:", this.props)
}
render() {
const { forwardedRef, ...rest } = this.props
// 将自定义的 prop 属性 “forwardedRef” 定义为 ref
return <Component ref={forwardedRef} {...rest} />
}
}
// 注意 React.forwardRef 回调的第二个参数 “ref”。
// 我们可以将其作为常规 prop 属性传递给 LogProps,例如 “forwardedRef”
// 然后它就可以被挂载到被 LogPros 包裹的子组件上。
return React.forwardRef((props, ref) => {
return <LogProps {...props} forwardedRef={ref} />
})
}
再 devTool 中显示名称
// 在 DevTools 中为该组件提供一个更有用的显示名,使用displayName。
// 例如 “ForwardRef(logProps(MyComponent))”
const name = Component.displayName || Component.name
forwardRef.displayName = `logProps(${name})`
return React.forwardRef(forwardRef)