如何在基于类的组件中使用React.forwardRef?


112

我正在尝试使用React.forwardRef,但是在如何使它在基于类的组件(而不是HOC)中工作的过程中遇到了麻烦。

该文档示例使用元素和功能组件,甚至将类包装在功能较高的组件中。

因此,从像这样在他们的ref.js文件:

const TextInput = React.forwardRef(
    (props, ref) => (<input type="text" placeholder="Hello World" ref={ref} />)
);

而是将其定义如下:

class TextInput extends React.Component {
  render() {
    let { props, ref } = React.forwardRef((props, ref) => ({ props, ref }));
    return <input type="text" placeholder="Hello World" ref={ref} />;
  }
}

要么

class TextInput extends React.Component {
  render() { 
    return (
      React.forwardRef((props, ref) => (<input type="text" placeholder="Hello World" ref={ref} />))
    );
  }
}

只工作:/

另外,我知道我知道,裁判不是反应方式。我正在尝试使用第三方画布库,并希望将其一些工具添加到单独的组件中,因此我需要事件侦听器,因此需要生命周期方法。稍后可能会走不同的路线,但是我想尝试一下。

文档说这是可能的!

引用转发不限于DOM组件。您也可以将引用转发到类组件实例。

本节注释中。

但是随后他们继续使用HOC,而不仅仅是类。

Answers:


108

始终使用相同道具的想法ref可以通过使用帮助程序代理类导出来实现。

class ElemComponent extends Component {
  render() {
    return (
      <div ref={this.props.innerRef}>
        Div has ref
      </div>
    )
  }
}

export default React.forwardRef((props, ref) => <ElemComponent 
  innerRef={ref} {...props}
/>);

因此,基本上,是的,我们被迫拥有其他支持转发引用的道具,但这可以在中心下完成。公众将其用作常规参考非常重要。


4
如果您将组件包装在像React-Redux connect或marterial-ui 这样的HOC中,该怎么办withStyles
J. Hesters '18

connect或有withStyles什么问题?您应该forwardRef使用HOC包装所有HOC,并在内部使用“安全”属性将ref传递给链中最低的组件。
Br Br '18

在使用setState时如何在酶中对此进行测试的任何见解?
zero_cool

抱歉,我需要更多详细信息。不确定到底是什么问题。
Br Br先生'18

2
我得到:永久违反:对象作为React子对象无效(找到:带有键{$$ typeof,render}的对象)。如果要渲染子级集合,请改用数组。
Brian Loughnane

9
class BeautifulInput extends React.Component {
  const { innerRef, ...props } = this.props;
  render() (
    return (
      <div style={{backgroundColor: "blue"}}>
        <input ref={innerRef} {...props} />
      </div>
    )
  )
}

const BeautifulInputForwardingRef = React.forwardRef((props, ref) => (
  <BeautifulInput {...props} innerRef={ref}/>
));

const App = () => (
  <BeautifulInputForwardingRef ref={ref => ref && ref.focus()} />
)

您需要使用其他道具名称来将引用转发到类。innerRef在许多库中常用。


在您的代码中,beautifulInputElement它更像是一个函数而不是React元素,应该像这样const beautifulInputElement = <BeautifulInputForwardingRef ref={ref => ref && ref.focus()} />
OlivierBoissé18-10-6

8

基本上,这只是一个HOC函数。如果您想在课堂上使用它,则可以自己做,并使用常规道具。

class TextInput extends React.Component {
    render() {
        <input ref={this.props.forwardRef} />
    }
}

const ref = React.createRef();
<TextInput forwardRef={ref} />

例如在中使用此模式,styled-components并在其中调用innerRef它。


3
使用像这样的不同名称的道具innerRef完全没有意义。我们的目标是完全透明:我应该能够将a <FancyButton>视为常规DOM元素,但是使用样式化组件方法时,我需要记住,该组件使用任意命名的ref代替prop ref
user128216

2
无论如何,styled-components都打算放弃对它的支持,innerRef以便使用React.forwardRef,将ref传递给孩子,这是OP试图实现的目的。
user128216

5

如果您愿意,可以使用更高阶的组件来实现:

import React, { forwardRef } from 'react'

const withForwardedRef = Comp => {
  const handle = (props, ref) =>
    <Comp {...props} forwardedRef={ref} />

  const name = Comp.displayName || Comp.name
  handle.displayName = `withForwardedRef(${name})`

  return forwardRef(handle)
}

export default withForwardedRef

然后在您的组件文件中:

class Boop extends React.Component {
  render() {
    const { forwardedRef } = this.props

    return (
      <div ref={forwardedRef} />
    )
  }
}

export default withForwardedRef(Boop)

我通过测试进行了前期工作,并为此发布了一个程序包react-with-forwarded-refhttps : //www.npmjs.com/package/react-with-forwarded-ref


3

如果您需要在许多不同的组件中重复使用此功能,则可以将此功能导出为类似 withForwardingRef

const withForwardingRef = <Props extends {[_: string]: any}>(BaseComponent: React.ReactType<Props>) =>
    React.forwardRef((props, ref) => <BaseComponent {...props} forwardedRef={ref} />);

export default withForwardingRef;

用法:

const Comp = ({forwardedRef}) => (
 <input ref={forwardedRef} />
)
const EnhanceComponent = withForwardingRef<Props>(Comp);  // Now Comp has a prop called forwardedRef
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.