Как использовать 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} />))
    );
  }
}

только работает: /

Кроме того, я знаю, что знаю, что ref - это не способ реакции. Я пытаюсь использовать стороннюю библиотеку холста и хочу добавить некоторые из их инструментов в отдельные компоненты, поэтому мне нужны прослушиватели событий, поэтому мне нужны методы жизненного цикла. Позже он может пойти другим путем, но я хочу попробовать это.

Документы говорят, что это возможно!

Ref forwarding is not limited to DOM components. You can forward refs to class component instances, too.

из обратите внимание в этом разделе.

Но затем они продолжают использовать HOC вместо классов.

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
127
0
68 872
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

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

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

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

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

Использование опоры с другим именем, такой как innerRef, полностью упускает суть. Цель - полная прозрачность: я должен иметь возможность рассматривать <FancyButton>, как если бы это был обычный элемент DOM, но, используя подход стилизованных компонентов, мне нужно помнить, что этот компонент использует опору с произвольным именем для ссылок, а не просто ref.

user5670895 17.08.2018 23:13

В любом случае, styled-components намереваются отказаться от поддержки innerRef в пользу передачи ссылки потомку с помощью React.forwardRef, что и пытается выполнить OP.

user5670895 17.08.2018 23:19
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()} />

Olivier Boissé 06.10.2018 15:53
Ответ принят как подходящий

Идея всегда использовать одну и ту же опору для ref может быть реализована путем проксирования экспорта класса с помощью помощника.

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

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

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

Что делать, если у вас есть компонент, обернутый в HOC, например connect React-Redux или withStyles marterial-ui?

J. Hesters 10.11.2018 19:00

В чем проблема с connect или withStyles? Вы должны заключить все HOC в forwardRef и внутренне использовать "безопасную" опору, чтобы передать ссылку на самый нижний компонент в цепочке.

Mr Br 19.11.2018 11:06

Любое представление о том, как проверить это в Enzyme, когда вы используете setState?

zero_cool 27.11.2018 00:21

Извините, мне нужно немного больше деталей. Не уверен, в чем именно проблема.

Mr Br 30.11.2018 13:13

Я получаю: Неизменяемое нарушение: объекты недопустимы в качестве дочерних элементов React (найдено: объект с ключами {$$ typeof, render}). Если вы хотели отобразить коллекцию дочерних элементов, используйте вместо этого массив.

Brian Loughnane 30.04.2019 21:38

@ J.Hesters решение не будет работать для withStyles, потому что withStyles внутренне использует innerRef, просто переименуйте свою собственную опору ref во что-то другое, например containerInnerRef, и все готово.

Alex Zinkevych 07.11.2019 11:46

@MrBr Как именно вы реализуете экспорт с React.forwardRef в компонент, обернутый connect redux? Я не совсем понимаю, что вы имели в виду, говоря об упаковке всех HOC в forwardRef. Я пытался это сделать, но все равно безуспешно. Вот как я это делаю: export default React.forwardRef((props, ref) => connect( mapStateToProps, mapDispatchToProps )(<ElemComponent innerRef = {ref} {...props} />) );

KAYLE 14.04.2020 19:37

@KAYLE, вы неправильно используете соединение. connect(...)((props) => <Elem innerRef = {ref} {props}/>)

Mr Br 15.04.2020 11:37

@MrBr, где в этом React.forwardRef?

KAYLE 15.04.2020 12:19

@KAYLE, как это было в вашем комментарии, просто обновите часть подключения.

Mr Br 15.04.2020 12:37

@MrBr Итак, вот что я сделал: export default React.forwardRef( connect( mapStateToProps, mapDispatchToProps )((props, ref) => <ElemComponent innerRef = {ref} {...props} />) );, и я получил эту ошибку: Uncaught (в обещании) TypeError: невозможно вызвать класс как функцию

KAYLE 15.04.2020 13:13

Да, у меня Uncaught TypeError: (0 , _hocName.default) is not a function

Yourin 03.08.2020 20:16

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

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

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

Другие вопросы по теме