Как использовать React.forwardRef в компоненте на основе класса?

Я пытаюсь использовать 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 вместо просто классов.

Ответы

Ответ 1

Идея всегда использовать одну и ту же опору для 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}
/>);

Итак, в основном, да, мы вынуждены иметь другую опору для пересылки реф, но это можно сделать под хабом. Важно, чтобы публика использовала его как нормальный реф.

Ответ 2

В принципе, это просто функция HOC. Если вы хотите использовать его с классом, вы можете сделать это самостоятельно и использовать обычные реквизиты.

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

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

Этот шаблон используется, например, в styled-components и он называется innerRef.

Ответ 3

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 обычно используется во многих библиотеках.

Ответ 4

Это может быть выполнено с помощью компонента более высокого порядка, если вам нравится:

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-ref: https://www.npmjs.com/package/react-with-forwarded-ref

Ответ 5

В том случае, если вам нужно использовать это во многих компонентах различий, вы можете экспортировать эту способность во что-то вроде 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

Ответ 6

Это именно то, что говорится в ссылке. Он показывает, как forwardRef используется с компонентами DOM:

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 содержит ссылку на элемент DOM.

Можно использовать forwardRef с компонентами класса так же:

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

Где ref содержит ссылку на экземпляр класса.