Wie verwende ich React.forwardRef in einer klassenbasierten Komponente?

110

Ich versuche, React.forwardRef zu verwenden, stolpere aber darüber, wie es in einer klassenbasierten Komponente (nicht HOC) funktioniert.

In den Dokumentbeispielen werden Elemente und Funktionskomponenten verwendet, und es werden sogar Klassen in Funktionen für Komponenten höherer Ordnung eingeschlossen.

Also, beginnend mit so etwas wie dies in ihrer ref.jsDatei:

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

und stattdessen so etwas definieren:

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

oder

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

funktioniert nur: /

Ich weiß auch, dass Refs nicht die richtige Reaktion sind. Ich versuche, eine Canvas-Bibliothek eines Drittanbieters zu verwenden, und möchte einige ihrer Tools in separaten Komponenten hinzufügen. Daher benötige ich Ereignis-Listener und daher Lebenszyklusmethoden. Es kann später einen anderen Weg gehen, aber ich möchte dies versuchen.

Die Dokumente sagen, dass es möglich ist!

Die Weiterleitung von Refs ist nicht auf DOM-Komponenten beschränkt. Sie können Refs auch an Klassenkomponenteninstanzen weiterleiten.

aus dem Hinweis in diesem Abschnitt.

Aber dann verwenden sie HOCs anstatt nur Klassen.

Han Lazarus
quelle

Antworten:

106

Die Idee, immer die gleiche Requisite für die zu refverwenden, kann durch Proxy-Klassenexport mit einem Helfer erreicht werden.

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}
/>);

Im Grunde genommen sind wir gezwungen, eine andere Requisite zu haben, um den Ref weiterzuleiten, aber dies kann unter der Nabe erfolgen. Es ist wichtig, dass die Öffentlichkeit es als normalen Ref verwendet.

Herr Br
quelle
4
Was tun Sie, wenn Sie Ihre Komponente in ein HOC wie React-Redux connectoder Marterial-UI eingewickelt haben withStyles?
J. Hesters
Was ist das Problem mit connectoder withStyles? Sie sollten alle HOCs mit forwardRef"sicherer" Requisite umwickeln und diese intern verwenden, um einen Ref an die niedrigste Komponente in der Kette weiterzuleiten.
Herr Br
Gibt es einen Einblick, wie Sie dies in Enzyme testen können, wenn Sie setState verwenden?
zero_cool
Entschuldigung, ich brauche ein bisschen mehr Details. Ich bin mir nicht sicher, wo genau das Problem liegt.
Herr Br
2
Ich erhalte: Invariante Verletzung: Objekte sind als untergeordnetes Element nicht gültig (gefunden: Objekt mit Schlüsseln {$$ typeof, render}). Wenn Sie eine Sammlung von untergeordneten Elementen rendern möchten, verwenden Sie stattdessen ein Array.
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()} />
)

Sie müssen einen anderen Requisitennamen verwenden, um den Ref an eine Klasse weiterzuleiten. innerRefwird häufig in vielen Bibliotheken verwendet.

Sebastien Lorber
quelle
in Ihrem Code beautifulInputElementist eher eine Funktion als ein Reaktionselement, es sollte so seinconst beautifulInputElement = <BeautifulInputForwardingRef ref={ref => ref && ref.focus()} />
Olivier Boissé
8

Grundsätzlich ist dies nur eine HOC-Funktion. Wenn Sie es mit Klasse verwenden möchten, können Sie dies selbst tun und normale Requisiten verwenden.

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

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

Dieses Muster wird zum Beispiel in verwendet styled-componentsund dort aufgerufen innerRef.

Łukasz Wojciechowski
quelle
3
Bei der Verwendung einer Requisite mit einem anderen Namen innerReffehlt der Punkt völlig. Das Ziel ist vollständige Transparenz: Ich sollte in der Lage sein, ein <FancyButton>Element so zu behandeln, als wäre es ein reguläres DOM-Element. Bei Verwendung des Styled-Components-Ansatzes muss ich jedoch berücksichtigen, dass diese Komponente anstelle von nur eine beliebig benannte Requisite für Refs verwendet ref.
user128216
2
In jedem Fall beabsichtigen gestylte Komponenten, die Unterstützung für dieinnerRef Weitergabe des Verweises an das untergeordnete Kind zu streichen React.forwardRef, was das OP zu erreichen versucht.
user128216
5

Dies kann mit einer Komponente höherer Ordnung erreicht werden, wenn Sie möchten:

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

Und dann in Ihrer Komponentendatei:

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

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

export default withForwardedRef(Boop)

Ich habe die Arbeit im Voraus mit Tests gemacht und ein Paket dafür veröffentlicht react-with-forwarded-ref: https://www.npmjs.com/package/react-with-forwarded-ref

rpearce
quelle
3

Wenn Sie dies in vielen verschiedenen Komponenten wiederverwenden müssen, können Sie diese Fähigkeit in etwas wie exportieren withForwardingRef

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

export default withForwardingRef;

Verwendung:

const Comp = ({forwardedRef}) => (
 <input ref={forwardedRef} />
)
const EnhanceComponent = withForwardingRef<Props>(Comp);  // Now Comp has a prop called forwardedRef
orepor
quelle