Jak używać React.forwardRef w komponencie opartym na klasach?

113

Próbuję użyć React.forwardRef, ale potykam się, jak sprawić, by działał w komponencie opartym na klasach (nie HOC).

Przykłady dokumentacji wykorzystują elementy i komponenty funkcjonalne, a nawet zawijają klasy w funkcje dla komponentów wyższego rzędu.

Tak więc, zaczynając coś jak to w swoim ref.jspliku:

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

i zamiast tego zdefiniować to jako coś takiego:

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

lub

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

działa tylko: /

Wiem też, że wiem, sędziowie nie reagują. Próbuję użyć biblioteki kanw innej firmy i chciałbym dodać niektóre z ich narzędzi w oddzielnych składnikach, więc potrzebuję detektorów zdarzeń, więc potrzebuję metod cyklu życia. Może później pójść inną drogą, ale chcę tego spróbować.

Doktorzy mówią, że to możliwe!

Przekazywanie referencji nie jest ograniczone do komponentów DOM. Możesz również przekazywać odwołania do instancji komponentów klasy.

z uwagi w tej sekcji.

Ale potem używają KWR zamiast zwykłych klas.

Han Lazarus
źródło

Odpowiedzi:

108

Pomysł, aby zawsze używać tej samej właściwości, refmożna osiągnąć przez eksportowanie klas przez proxy z pomocą pomocnika.

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

Więc w zasadzie tak, jesteśmy zmuszeni mieć inny rekwizyt do ref do przodu, ale można to zrobić pod piastą. Ważne jest, aby publiczność używała go jako normalnego ref.

Pan Br
źródło
4
Co zrobisz, jeśli masz swój komponent zapakowany w HOC, taki jak React-Redux connectlub marterial-ui withStyles?
J. Hesters,
Jaki jest problem z connectlub withStyles? Powinieneś owinąć wszystkie HOC forwardRefi wewnętrznie użyć „bezpiecznej” podpórki, aby przekazać odniesienie do najniższego komponentu w łańcuchu.
Pan Br
Jakieś wskazówki, jak to sprawdzić w Enzyme, gdy używasz setState?
zero_cool
Przepraszam, potrzebuję trochę więcej szczegółów. Nie jestem pewien, na czym dokładnie polega problem.
Pan Br
2
Otrzymuję: Naruszenie niezmienności: Obiekty nie są poprawne jako element potomny React (znaleziono: obiekt z kluczami {$$ typeof, render}). Jeśli zamierzałeś renderować kolekcję elementów podrzędnych, użyj zamiast tego tablicy.
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()} />
)

Aby przekazać referencję do klasy, musisz użyć innej nazwy właściwości. innerRefjest powszechnie używany w wielu bibliotekach.

Sebastien Lorber
źródło
w twoim kodzie beautifulInputElementjest bardziej funkcją niż elementem React, powinno być takconst beautifulInputElement = <BeautifulInputForwardingRef ref={ref => ref && ref.focus()} />
Olivier Boissé
8

Zasadniczo jest to tylko funkcja HOC. Jeśli chciałeś użyć go z klasą, możesz to zrobić samodzielnie i użyć zwykłych rekwizytów.

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

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

Ten wzorzec jest używany na przykład w programie styled-componentsi jest innerReftam nazywany .

Łukasz Wojciechowski
źródło
3
Używanie rekwizytu o innej nazwie, na przykład, innerRefcałkowicie mija się z celem. Celem jest całkowita przejrzystość: powinienem być w stanie traktować element <FancyButton>tak, jakby był zwykłym elementem DOM, ale używając podejścia styled-components, muszę pamiętać, że ten komponent używa arbitralnie nazwanej właściwości referencyjnej zamiast po prostu ref.
user128216
2
W każdym razie styled-components zamierzają zrezygnować z obsługi nainnerRef rzecz przekazywania referencji dziecku używającego React.forwardRef, co próbuje osiągnąć OP.
user128216
5

Można to osiągnąć za pomocą komponentu wyższego rzędu, jeśli chcesz:

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

A następnie w pliku komponentu:

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

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

export default withForwardedRef(Boop)

Wykonałem pracę z góry z testami i opublikowałem pakiet do tego react-with-forwarded-ref: https://www.npmjs.com/package/react-with-forwarded-ref

rpearce
źródło
3

Jeśli musisz ponownie użyć tego w wielu różnych komponentach, możesz wyeksportować tę zdolność do czegoś podobnego withForwardingRef

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

export default withForwardingRef;

stosowanie:

const Comp = ({forwardedRef}) => (
 <input ref={forwardedRef} />
)
const EnhanceComponent = withForwardingRef<Props>(Comp);  // Now Comp has a prop called forwardedRef
orepor
źródło