React(精读官方文档) -高级指引 - Refs转发

Refs转发

概述

  • 将ref自动地通过组件传递到子组件的技巧
  • 父组件可以通过ref操作子组件,直接使用子组件的DOM

转发 refs 到 DOM 组件

  • 渲染原生 DOM 元素 button 的 FancyButton 组件(子组件)
function FancyButton(props) {
  return (
    <button className="FancyButton">
      {props.children}
    </button>
  )
}
  • Ref 转发是一个可选特性,其允许某些组件接收 ref,并将其向下传递给子组件
//子组件
const FancyButton = React.forwardRef((props, ref) => (
  <button ref={ref} className="FancyButton">
    {props.children}
  </button>
))

//父组件
//可以直接获取 DOM button 的 ref:
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;

//这样,在父组件中可以直接使用DOM button
  • 以下是对上述示例发生情况的逐步解释:
    • 通过调用 React.createRef 创建了一个 React ref 并将其赋值给 ref 变量
    • 指定 ref 为 JSX 属性,将其向下传递给 FancyButton
    • React传递ref给forwardRef作为其第二个参数
    • 向下转发ref参数到button,并将其指定为JSX属性
    • 当ref挂载完成,ref.current将指向button DOM节点

组件库维护者的注意事项

  • 当使用forwardRf时,应将其视为一个新的主版本

在高阶组件中转发 refs

  • 转发ref对高阶组件是很有用的,让我们从一个输出组件props到控制台的高阶组件为例

     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;
     }
    
  • logProps组件是一个高阶组件,props将会传递到其包裹的组件。这个高阶组件可以记录所有传递到fancyButton组件的props

    class FancyButton extends React.Component {
         focus() {
           // ...
         }
       
         // ...
    }
       
       // 我们导出 LogProps,而不是 FancyButton。
       // 虽然它也会渲染一个 FancyButton。
       export default logProps(FancyButton)
    
  • 需要注意:refs 将不会透传下去。这是因为 ref 不是 prop 属性。就像 key 一样,其被 React 进行了特殊处理

  • 如果对 HOC 添加 ref,该 ref 将引用最外层的容器组件,而不是被包裹的组件

  • 意味着用于我们 FancyButton 组件的 refs 实际上将被挂载到 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}
       />;
    
  • 解决办法:可以使用 React.forwardRef API 明确地将 refs 转发到内部的 FancyButton 组件。React.forwardRef 接受一个渲染函数,其接收 props 和 ref 参数并返回一个 React 节点

    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”
      // 然后它就可以被挂载到被 LogProps 包裹的子组件上。
      return React.forwardRef((props, ref) => {
        return <LogProps {...props} forwardedRef={ref} />;
      });
    }
    

在 DevTools 中显示自定义名称

  • 下面的组件将在DevTools中显示为“ForwardRef”
const WrappedComponent = React.forwardRef((props, ref) => {
  return <LogProps {...props} forwardedRef={ref} />;
})
  • 如果命名了渲染函数,DevTools 也将包含其名称(例如 “ForwardRef(myFunction)”)
const WrappedComponent = React.forwardRef(
  function myFunction(props, ref) {
    return <LogProps {...props} forwardedRef={ref} />;
  }
)
  • 设置函数的 displayName 属性来包含被包裹组件的名称
function logProps(Component) {
  class LogProps extends React.Component {
    // ...
  }

  function forwardRef(props, ref) {
    return <LogProps {...props} forwardedRef={ref} />;
  }

  // 在 DevTools 中为该组件提供一个更有用的显示名。
  // 例如 “ForwardRef(logProps(MyComponent))”
  const name = Component.displayName || Component.name;
  forwardRef.displayName = `logProps(${name})`;

  return React.forwardRef(forwardRef);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值