We want to hear from you!Take our 2021 Community Survey!

Inoltrare Refs

L’inoltro delle ref è una tecnica per passare automaticamente una ref attraverso un componente ad uno dei suoi figli. Tipicamente questo non è necessario per la maggior parte dei componenti dell’applicazione. Può comunque essere molto utile per alcuni tipi di componenti, specialmente per i componenti riusabili appartenenti alle librerie. Lo scenario più comune è descritto di seguito.

Inoltro delle refs ai componenti del DOM

Considera di avere un componente FancyButton che renderizza l’elemento nativo del DOM button :

function FancyButton(props) {
  return (
    <button className="FancyButton">
      {props.children}
    </button>
  );
}

I componenti React nascondono i dettagli della loro implementazione, incluso il loro output renderizzato. Altri componenti che usano FancyButton solitamente non hanno bisogno di ottenere una ref dell’elemento interno del DOM button. Ciò è ottimo perché previene ai componenti di affidarsi troppo alla struttura DOM l’uno dell’altro.

Sebbene questa incapsulazione sia desiderabile per componenti a livello applicativo come FeedStory o Comment, può essere conveniente per componenti “foglia” altamente riutilizzabili come FancyButton o MyTextInput. Questi componenti tendono ad essere utilizzati attraverso l’applicazione in modo simile agli elementi del DOM button e input e l’accesso ai loro nodi DOM può essere inevitabile per la gestione di messa a fuoco, selezione, o animazioni.

L’inoltro delle refs è una feature opt-in che permette ad alcuni componenti di prendere le ref che ricevono e passarle in basso (in altre parole avanti) ai suoi figli.

Nell’esempio seguente, FancyButton utilizza React.forwardRef per ottenere la ref passata, dopodiché la passa all’elemento del DOM button che la renderizza:

const FancyButton = React.forwardRef((props, ref) => (  <button ref={ref} className="FancyButton">    {props.children}
  </button>
));

// You can now get a ref directly to the DOM button:
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;

In questo modo i componenti che usano FancyButton possono ottenere una ref al nodo del DOM button e accedervi, se necessario, proprio come se utilizzassero l’elemento del DOM button direttamente.

Qui puoi trovare una spiegazione passo passo di quello che succede nel precedente esempio:

  1. Creiamo una ref React chiamando React.createRef e assegnandola ad una variabile ref.
  2. Passiamo la nostra ref a <FancyButton ref={ref}> specificandola come un attributo JSX.
  3. React passa la ref alla funzione (props, ref) => ... all’interno di forwardRef come secondo argomento.
  4. Passiamo questo argomento ref a <button ref={ref}> specificandolo come attributo JSX.
  5. Quando la ref è attaccata, ref.current punterà all’elemento del DOM button.

Nota

Il secondo argomento ref esiste solamente quando viene definito un componente con la chiamata React.forwardRef. I componenti di tipo classe e di tipo funzione non ricevono l’argomento ref e la ref non è disponibile nemmeno nelle props.

L’inoltro delle Ref non è limitato ai componenti del DOM. Possiamo passare le refs anche ad istanze di componenti classe.

Nota per i mantenitori di librerie di componenti

Quando inizi ad usare forwardRef nelle librerie di componenti, dovresti trattarlo come una breaking change e rilasciare una nuova versione della libreria. Questo perché la tua libreria probabilmente ha un comportamento diverso (ad esempio nel modo in cui le ref sono assegnate e i tipi che sono esportati), e questo può rompere applicazioni e altre librerie che dipendono da questo vecchio comportamento.

Applicare in modo condizionale le React.forwardRef quando esistono non è raccomandato per alcune ragioni: cambiano il comportamento della tua libreria e possono rompere le applicazioni degli utenti quando questi aggiornano React.

Inoltro delle refs in componenti di tipo higher-order

Questa tecnica può anche essere particolarmente utile con componenti di tipo higher-order (conosciuti anche come HOC). Iniziamo con un esempio che logga le props di un componente in console:

function logProps(WrappedComponent) {  class LogProps extends React.Component {
    componentDidUpdate(prevProps) {
      console.log('old props:', prevProps);
      console.log('new props:', this.props);
    }

    render() {
      return <WrappedComponent {...this.props} />;    }
  }

  return LogProps;
}

L’HOC “logProps” passa tutte le props attraverso il componente che lo contiene, in questo modo l’output renderizzato sarà il solito. Ad esempio possiamo usare l’HOC per stampare tutte le props passate al componente FancyButton:

class FancyButton extends React.Component {
  focus() {
    // ...
  }

  // ...
}

// Rather than exporting FancyButton, we export LogProps.
// It will render a FancyButton though.
export default logProps(FancyButton);

C’è un avvertimento all’esempio precedente: le refs non saranno passate attraverso di esso. Questo perché una ref non è una prop. Come la key, è maneggiata in modo diverso da React. Se vuoi aggiungere una ref ad un HOC, la ref dovrà riferirsi ad un componente container più esterno, non al componente che lo contiene.

Questo significa che le refs per il nostro componente FancyButton saranno attaccate al componente LogProps:

import FancyButton from './FancyButton';

const ref = React.createRef();
// The FancyButton component we imported is the LogProps HOC.
// Even though the rendered output will be the same,
// Our ref will point to LogProps instead of the inner FancyButton component!
// This means we can't call e.g. ref.current.focus()
<FancyButton
  label="Click Me"
  handleClick={handleClick}
  ref={ref}/>;

Fortunatamente possiamo passare le refs esplicitamente al componente interno FancyButton utilizzando le API React.forwardRef. React.forwardRef accetta una funzione di renderizzazione che riceve le props e le ref come parametri e le ritorna ad un nodo React. Ad esempio:

function logProps(Component) {
  class LogProps extends React.Component {
    componentDidUpdate(prevProps) {
      console.log('old props:', prevProps);
      console.log('new props:', this.props);
    }

    render() {
      const {forwardedRef, ...rest} = this.props;
      // Assign the custom prop "forwardedRef" as a ref
      return <Component ref={forwardedRef} {...rest} />;    }
  }

  // Note the second param "ref" provided by React.forwardRef.
  // We can pass it along to LogProps as a regular prop, e.g. "forwardedRef"
  // And it can then be attached to the Component.
  return React.forwardRef((props, ref) => {    return <LogProps {...props} forwardedRef={ref} />;  });}

Mostrare un nome custom negli strumenti dello sviluppatore

React.forwardRef accetta una funzione di renderizzazione. Gli strumenti per gli sviluppatori di React utilizzano questa funzione per determinare cosa mostrare per l’inoltro al componente.

Ad esempio, il seguente componente apparirà come ”ForwardRef” negli strumenti per sviluppatori:

const WrappedComponent = React.forwardRef((props, ref) => {
  return <LogProps {...props} forwardedRef={ref} />;
});

Se assegni un nome alla funzione di renderizzazione, gli strumenti per sviluppatori includeranno anche il suo nome (ad esempio ”ForwardRef(myFunction)“)

const WrappedComponent = React.forwardRef(
  function myFunction(props, ref) {
    return <LogProps {...props} forwardedRef={ref} />;
  }
);

Puoi anche settare la proprietà displayName della funzione per includere il componente che stai avvolgendo:

function logProps(Component) {
  class LogProps extends React.Component {
    // ...
  }

  function forwardRef(props, ref) {
    return <LogProps {...props} forwardedRef={ref} />;
  }

  // Give this component a more helpful display name in DevTools.
  // e.g. "ForwardRef(logProps(MyComponent))"
  const name = Component.displayName || Component.name;  forwardRef.displayName = `logProps(${name})`;
  return React.forwardRef(forwardRef);
}

Is this page useful?Edit this page