Як використовувати 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. Ви також можете пересилати посилання на екземпляри компонентів класу.

з примітки цього розділу.

Але потім вони продовжують використовувати HOCs, а не просто заняття.

Відповіді:


108

Ідея завжди використовувати одну і ту ж опору для Can 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
Що ви робите, якщо ваш компонент загорнутий у HOC на зразок React-Redux connectабо marterial-ui withStyles?
Дж. Гестерс

У чому проблема connectабо withStyles? Ви повинні обернути всі ГОС forwardRefта використовувати внутрішньо "безпечну" опору для передачі списку найнижчому компоненту ланцюга.
Містер Бр

Будь-яке розуміння, як перевірити це в Enzyme, коли ви використовуєте setState?
zero_cool

Вибачте, мені знадобиться трохи більше деталей. Не впевнений, в чому саме проблема.
Пан Бр

2
Я отримую: Інваріантне порушення: Об'єкти недійсні як дитина-реагувач (знайдено: об'єкт з ключами {$$ typeof, render}). Якщо ви мали намір надати колекцію дітей, використовуйте замість цього масив.
Брайан

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- це скоріше функція, а не Реактивний елемент, воно має бути такимconst beautifulInputElement = <BeautifulInputForwardingRef ref={ref => ref && ref.focus()} />
Олів'є Боассе

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повністю не вистачає сенсу. Мета - повна прозорість: я повинен мати можливість ставитись до <FancyButton>як би до звичайного елемента DOM, але, використовуючи підхід стильових компонентів, мені потрібно пам’ятати, що цей компонент використовує довільну підтримку для refs замість просто ref.
користувач128216

2
У будь-якому випадку, стильові компоненти планують відмовитись у підтримці наinnerRef користь передачі виклику дитині React.forwardRef, яка використовує , що саме ОП намагається досягти.
користувач128216

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-ref: https://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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.