Refs转发(forwardRef)

2,466 阅读2分钟

Ref转发是一项将ref自动地通过组件传递到其一子组件的技巧(允许某些组件接收ref,并将其向下传递给子组件).

对一些高可复用的“叶”组件,倾向于在整个应用中以一种常规DOM button和input的方式被使用,并且访问其DOM节点,对焦点的管理、选中或动画来说是不可避免的。

FunctionButton使用React.forwardRef来获取传递给它的ref,然后转发到它渲染的DOM Button,

const FancyButton = React.forwardRef((props, ref) => (
    <button ref={ref}>{props.children}</button>
))

// 你可以直接获取DOM button的ref
const ref = React.createRef();
<FancyButton ref={ref}>click me</FancyButton>

实现过程

  1. 我们通过调用 React.createRef 创建了一个 React ref 并将其赋值给 ref 变量。
  2. 我们通过指定 ref 为 JSX 属性,将其向下传递给 。
  3. React 传递 ref 给 forwardRef 内函数 (props, ref) => ...,作为其第二个参数。
  4. 我们向下转发该 ref 参数到 <button ref={ref}>,将其指定为 JSX 属性。
  5. 当 ref 挂载完成,ref.current 将指向 <button> DOM 节点

第二个参数只在使用React.forwardRef定义组件时存在,常规函数和class组件不接收ref参数,且props中也不存在ref

在高阶组件中转发

在高阶组件上定义ref, refs是不会传递下去的,这是因为ref不是props属性,就像key一样,其被React做了特殊处理,对HOC添加ref,该ref将引用最外层容器组件,而不是被包裹的组件。

const hoc = WrapperComponent => React.forwardRef((props, ref) => {
    return <WrapperComponent {...props} countryRef={ref} />
})

function Country({label, countryRef}) {
    return (
        <div>
            <label>{label}</label>
            <input ref={countryRef}/>
        </div>
    )
}
// 使用
const HocCountry = hoc(Country);
<HocCountry ref={this.countryRef} />
    

还可以和context一起使用

<!--GGEditor中的一段代码-->

import React from 'react';
import { Graph } from '@/common/interface';

const EditorContext = React.createContext({});

export const withEditorContext = function(WrappedComponent) {


  const InjectEditorContext = props => {
    const { forwardRef, ...rest } = props;

    return (
      <EditorContext.Consumer>
        {context => <WrappedComponent ref={forwardRef} {...rest} {...context} />}
      </EditorContext.Consumer>
    );
  };

  return React.forwardRef((props, ref) => (
    <InjectEditorContext forwardRef={ref} {...props} />
  ));
};

export default EditorContext;