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



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


По сути, это просто функция HOC. Если вы хотите использовать его с классом, вы можете сделать это самостоятельно и использовать обычные реквизиты.
class TextInput extends React.Component {
render() {
<input ref = {this.props.forwardRef} />
}
}
const ref = React.createRef();
<TextInput forwardRef = {ref} />
Этот шаблон используется, например, в styled-components и там он называется innerRef.
В любом случае, styled-components намереваются отказаться от поддержки innerRef в пользу передачи ссылки потомку с помощью React.forwardRef, что и пытается выполнить OP.
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()} />
Идея всегда использовать одну и ту же опору для 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?
В чем проблема с connect или withStyles? Вы должны заключить все HOC в forwardRef и внутренне использовать "безопасную" опору, чтобы передать ссылку на самый нижний компонент в цепочке.
Любое представление о том, как проверить это в Enzyme, когда вы используете setState?
Извините, мне нужно немного больше деталей. Не уверен, в чем именно проблема.
Я получаю: Неизменяемое нарушение: объекты недопустимы в качестве дочерних элементов React (найдено: объект с ключами {$$ typeof, render}). Если вы хотели отобразить коллекцию дочерних элементов, используйте вместо этого массив.
@ J.Hesters решение не будет работать для withStyles, потому что withStyles внутренне использует innerRef, просто переименуйте свою собственную опору ref во что-то другое, например containerInnerRef, и все готово.
@MrBr Как именно вы реализуете экспорт с React.forwardRef в компонент, обернутый connect redux? Я не совсем понимаю, что вы имели в виду, говоря об упаковке всех HOC в forwardRef. Я пытался это сделать, но все равно безуспешно. Вот как я это делаю: export default React.forwardRef((props, ref) => connect( mapStateToProps, mapDispatchToProps )(<ElemComponent innerRef = {ref} {...props} />) );
@KAYLE, вы неправильно используете соединение. connect(...)((props) => <Elem innerRef = {ref} {props}/>)
@MrBr, где в этом React.forwardRef?
@KAYLE, как это было в вашем комментарии, просто обновите часть подключения.
@MrBr Итак, вот что я сделал: export default React.forwardRef( connect( mapStateToProps, mapDispatchToProps )((props, ref) => <ElemComponent innerRef = {ref} {...props} />) );, и я получил эту ошибку: Uncaught (в обещании) TypeError: невозможно вызвать класс как функцию
Да, у меня Uncaught TypeError: (0 , _hocName.default) is not a function
Если вам нужно повторно использовать это во многих различных компонентах, вы можете экспортировать эту возможность во что-то вроде 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
Использование опоры с другим именем, такой как
innerRef, полностью упускает суть. Цель - полная прозрачность: я должен иметь возможность рассматривать<FancyButton>, как если бы это был обычный элемент DOM, но, используя подход стилизованных компонентов, мне нужно помнить, что этот компонент использует опору с произвольным именем для ссылок, а не простоref.