Reenviament de Refs
El reenviament de referències és una tècnica per passar automàticament una ref a través d’un component a un dels seus fills. Això normalment no és necessari per a la majoria dels components de l’aplicació. Tanmateix, pot ser útil per a alguns tipus de components, especialment en biblioteques de components reutilitzables. Els escenaris més comuns es descriuen a continuació.
Reenviament de referències als components del DOM
Considera un component (BotoElegant)FancyButton
que renderitza l’element button
nadiu del DOM:
function FancyButton(props) {
return (
<button className="FancyButton">
{props.children}
</button>
);
}
Els components de React amaguen els seus detalls d’implementació, incloent la seva sortida renderitzada. Altres components que fan servir el FancyButton
normalment no caldrà que obtenguin una ref a l’element intern button
del DOM. Això és bo perquè evita que els components es basin massa en l’estructura DOM dels altres.
Encara que aquesta encapsulació és desitjable per a components de nivell d’aplicació com ElMeuTema
o Comentari
, pot ser inconvenient per a components molt reutilitzables com BotoElegant
o ElMeuInputDeText
. Aquests components tendeixen a ser utilitzats en tota l’aplicació d’una manera similar a un button
i input
DOM, i accedir als seus nodes DOM pot ser inevitable per gestionar el focus, la selecció o les animacions.
El reenviament de ref és una característica opcional que permet a alguns components prendre una ref
que reben, i passar-la més avall (en altres paraules, “reenviant” a un component fill.
A l’exemple de sota el component (BotoElegant) FancyButton
utilitza React.forwardRef
per obtenir la ref
que se li ha passat, i després es dirigeix al button
del DOM que el renderitza:
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>;
D’aquesta manera, els components que utilitzen FancyButton
poden obtenir una referència al node subjacent button
del DOM i accedir-hi si és necessari, igual que si utilitzen un button
del DOM directament.
A continuació hi trobaràs una explicació pas a pas del que passa a l’exemple anterior:
- Creem una ref de React cridant
React.createRef
i assignant-la a una variableref
. - Passem la nostra
ref
a<FancyButton ref={ref}>
especificant-la com un atribut JSX. - React passa la
ref
a la funció(props, ref) => ...
dins deforwardRef
com a segon argument. - Reenviem aquest argument
ref
avall fins a<button ref={ref}>
lligant-lo com un atribut JSX. - Quan la referència estigui lligada ,
ref.current
apuntarà al node<button>
del DOM.
Nota
El segon argument
ref
només existeix quan definiu un component amb la cridaReact.forwardRef
. Ni les funcions normals ni els components de classe reben l’argumentref
, i la referència tampoc està disponible en lesprops
.El reenviament de referències no està limitat als components del DOM. També podeu reenviar referències a instàncies de components de classe.
Nota pels mantenidors de biblioteques de components
Quan comencis a fer servir forwardRef
en una biblioteca de components hauries de tractar-la com un canvi incompatible i publicar una nova versió major de la teva biblioteca. Això es deu al fet que la teva biblioteca probablement tindrà un comportament molt diferent (com ara a quins elements s’assignen les refs i a quins tipus s’exporten), i això pot fer incompatibles altres aplicacions i biblioteques que depenen del comportament antic.
Aplicar condicionadament React.forwardRef
quan existeix no es recomana per les mateixes raons: canvia el comportament de la biblioteca i pot fer incompatibles les aplicacions dels usuaris quan actualitzin el mateix React.
Reenviament de Referències en components d’ordre superior
Aquesta tècnica també pot ser particularment útil amb components d’ordre superior (també coneguts com a HOCs). Comencem amb un exemple HOC que mostra a console les props
dels components que embolica:
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” mostra totes les props
del component que embolica, de manera que la sortida renderitzada serà la mateixa. Per exemple, podem utilitzar aquest HOC per mostrar totes les props
que es passen al nostre component de “botó elegant”:
class FancyButton extends React.Component {
focus() {
// ...
}
// ...
}
// Rather than exporting FancyButton, we export LogProps.
// It will render a FancyButton though.
export default logProps(FancyButton);
Hi ha, però, un problema a l’exemple anterior: les referències no es passaran. Això és perquè ref
no és una prop
. Igual que key
, és gestionat de manera diferent per React. Si afegiu una referència a un HOC, la referència es refereix al component de contenidor més extern, no al component embolicat.
Això significa que les referències destinades al nostre component FancyButton
s’afegiran al component 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}/>;
Per sort, però, podem reenviar explícitament les referències al component interior FancyButton
utilitzant l’API React.forwardRef
. React.forwardRef
accepta una funció de renderització que rep els paràmetres props
i ref
i retorna un node React. Per exemple:
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} />; });}
Mostrar un nom personalitzat a DevTools
React.forwardRef
accepta una funció de renderitzat. React DevTools utilitza aquesta funció per saber què ha de mostrar del component que fa el reenviament.
Per exemple, el component següent apareixerà com a ”ForwardRef” en les DevTools:
const WrappedComponent = React.forwardRef((props, ref) => {
return <LogProps {...props} forwardedRef={ref} />;
});
Si dones nom a la funció de renderització, DevTools també n’inclourà el seu nom (e.g. ”ForwardRef(myFunction)”):
const WrappedComponent = React.forwardRef(
function myFunction(props, ref) {
return <LogProps {...props} forwardedRef={ref} />;
}
);
Fins i tot pots establir la propietat displayName
de la funció per incloure el component que està embolicat:
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);
}