Comment démarrer rapidement avec VUE3.0 : entrez et apprenez
Dans les projets React, il existe de nombreux scénarios dans lesquels Ref
est nécessaire. Par exemple, utilisez ref
pour obtenir le nœud DOM et obtenez l'instance d'objet ClassComponent ; utilisez useRef
Hook pour créer un objet Ref afin de résoudre le problème de l'impossibilité pour setInterval
d'obtenir le dernier état, vous pouvez également appeler React.createRef
méthode pour créer manuellement un objet Ref
.
Bien que Ref
soit très simple à utiliser, il est toujours inévitable de rencontrer des problèmes dans les projets réels. Cet article réglera divers problèmes liés à Ref
du point de vue du code source et clarifiera ce qui est fait derrière ref
. Après avoir lu cet article, vous aurez peut-être une meilleure compréhension de Ref
.
Tout d'abord, ref
est l'abréviation de reference
, qui est une référence. Dans le fichier de déclaration de type react
, vous pouvez trouver plusieurs types liés à Ref, et ils sont répertoriés ici.
RefObject<T> { lecture seule actuelle : T null } interface MutableRefObject<T> { current: T; }
Lors de l'utilisation de useRef
Hook , RefObject RefObject
MutableRefObejct est renvoyé. Les deux types définissent une structure d'objet de { current: T }
. , Typescript avertira ⚠️ si refObject.current
est modifié.
const ref = useRef<string>(null) ref.current = '' // Erreur
TS : ne peut pas être attribué à "current" car il s'agit d'une propriété en lecture seule.
Regardez la définition de la méthode useRef
. La surcharge de fonction est utilisée ici. Lorsque le paramètre générique entrant T
ne contient pas null
, RefObject<T>
est renvoyé. Lorsqu'il contient null
, MutableRefObject<T>
est renvoyé.
fonction useRef<T>(initialValue : T) : MutableRefObject<T>; function useRef<T>(initialValue: T | null): RefObject<T>;
Donc, si vous souhaitez que la propriété actuelle de l'objet ref créé soit modifiable, vous devez ajouter | null
.
const ref = useRef<string | null>(null) ref.current = '' // OK,
lors de l'appel de la méthode React.createRef()
, un RefObject
est également renvoyé.
createRef
createRef() : RefObject { const refObject = { actuel : nul, } ; si (__DEV__) { Objet.seal(refObject); } return refObject; }
RefObject/MutableRefObject
a été ajouté dans la version 16.3
. Si vous utilisez des versions antérieures, vous devez utiliser Ref Callback
.
Utiliser Ref Callback
consiste à transmettre une fonction de rappel lorsque React rappelle, l'instance correspondante sera renvoyée et elle peut être enregistrée par elle-même pour l'appel. Le type de cette fonction de rappel est RefCallback
.
tapez RefCallback<T> = (instance : T | null) => void
Exemple d'utilisation RefCallback
:
import React from 'react' la classe d'exportation CustomTextInput étend React.Component { texteInput : HTMLInputElement | null = null ; saveInputRef = (élément : HTMLInputElement | null) => { this.textInput = élément ; } rendre() { retour ( <input type="text" ref={this.saveInputRef} /> ); } }
Dans la déclaration de type, il existe également des types Ref/LegacyRef, qui sont utilisés pour faire référence au type Ref en général. LegacyRef
est une version compatible dans l'ancienne version précédente, ref
pouvait également être une chaîne.
tapez Ref<T> = RefCallback<T> | RefObject<T> null ; tapez LegacyRef<T> = string | Ref<T>;
Ce n'est qu'en comprenant les types liés à Ref que vous pourrez devenir plus à l'aise pour écrire Typescript.
PasserLors de l'utilisation ref
sur un composant JSX, nous définissons une Ref
à l'attribut ref
. Nous savons tous que la syntaxe de jsx
sera compilée sous la forme createElement
par des outils tels que Babel.
//jsx <App ref={ref} id="my-app" ></App> // compilé vers React.createElement(Application, { réf: réf, identifiant : "mon-application" });
Il semble que ref
ne soit pas différent des autres accessoires, mais si vous essayez d'imprimer props.ref à l'intérieur du composant, ce undefined
. Et la console de l'environnement dev
affichera des invites.
En essayant d'y accéder,
undefined
sera renvoyé. Si vous devez accéder à la même valeur dans le composant enfant, vous devez la transmettre en tant qu'accessoire différent.
Que fait React avec ref ? Comme vous pouvez le voir dans le code source de ReactElement, ref
est RESERVED_PROPS
. key
bénéficient également de ce traitement. Elles seront spécialement traitées et extraites des accessoires et transmises à Element
.
const RESERVED_PROPS = { clé : vrai, réf : vrai, __lui-même : vrai, __source : vrai, };
Donc ref
est “props“
qui sera traité spécialement.
Avant la version 16.8.0
, le composant de fonction était sans état et ne s'affichait qu'en fonction des accessoires entrants. Avec Hook, vous pouvez non seulement avoir un état interne, mais également exposer des méthodes pour les appels externes (nécessitant forwardRef
et useImperativeHandle
).
Si vous utilisez ref
directement pour un Function Component
, la console de l'environnement de développement vous avertira que vous devez l'envelopper avec forwardRef
.
fonctionEntrée() { retourner <entrée /> } const ref = useRef() <Input ref={ref} />
Les composants de fonction ne peuvent pas recevoir de références. Les tentatives d'accès à cette référence échoueront. Vouliez-vous utiliser React.forwardRef()
forwardRef
Affichez le code source ReactForwardRef.js. Pliez le code associé à __DEV__
. Il s'agit simplement d'un composant d'ordre élevé extrêmement simple. Recevez un FunctionComponent rendu, enveloppez-le et définissez $$typeof
comme REACT_FORWARD_REF_TYPE
, puis return
-le.
Suivez le code et recherchez solveLazyComponentTag, où $$typeof
sera analysé dans le WorkTag correspondant.
Le WorkTag correspondant à REACT_FORWARD_REF_TYPE
est ForwardRef. Ensuite, ForwardRef entrera dans la logique de updateForwardRef.
cas ForwardRef : { enfant = updateForwardRef ( nul, travail en cours, Composant, résoluProps, les voies de rendu, ); retourner l'enfant; }
Cette méthode appellera la méthode renderWithHooks et transmettra ref
dans le cinquième paramètre.
nextChildren = renderWithHooks ( actuel, travail en cours, rendre, prochainProps, ref, // ici renderLanes, );
Continuez à tracer le code et entrez la méthode renderWithHooks. Vous pouvez voir que ref
sera passé comme deuxième paramètre de Component
. À ce stade, nous pouvons comprendre d'où vient le deuxième paramètre ref
de FuncitonComponent
enveloppé par forwardRef
(par rapport au deuxième paramètre du constructeur ClassComponent qui est Context).
Sachant comment transmettre la référence, la question suivante est de savoir comment la référence est attribuée.
Le(attribuez un RefCallback à ref et cassez le point dans le rappel). Tracez le code commitAttachRef Dans cette méthode, il sera jugé si la référence du nœud Fibre est function
ou un RefObject, et l'instance. sera traité selon le type. Si le nœud Fibre est un HostComponent ( tag = 5
), qui est un nœud DOM, l'instance est le nœud DOM ; et si le nœud Fibre est un ClassComponent ( tag = 1
), l'instance est l'instance de l'objet ;
fonction commitAttachRef (finishedWork) { var ref = finishWork.ref; si (réf !== null) { var instanceToUse = finishWork.stateNode; if (typeof ref === 'fonction') { ref(instanceToUse); } autre { ref.current = instanceToUse ; } } }
Ce qui précède est la logique d'affectation de ref dans HostComponent et ClassComponent. Pour les composants de type ForwardRef, différents codes sont utilisés, mais le comportement est fondamentalement le même. Vous pouvez voir l'impératifHandleEffect ici.
Ensuite, nous continuons à approfondir le code source de React pour voir comment useRef est implémenté.
localise le code d'exécution useRef ReactFiberHooks en suivant le code
Il existe ici deux méthodes, mountRef
et updateRef
. Comme leur nom l'indique, elles correspondent aux opérations sur ref
lorsque Fiber
mount
et update
.
function updateRef<T>(initialValue : T): {|current : T|} { const hook = updateWorkInProgressHook(); return hook.memoizedState; } fonction mountRef<T>(initialValue : T) : {|current : T|} { const hook = mountWorkInProgressHook(); const ref = {actuel : valeurinitiale} ; hook.memoizedState = ref; renvoyer la référence ; }
Vous pouvez voir que lors mount
, useRef
crée un RefObject
et l'assigne au memoizedState
du hook
. Lors de update
, il est retiré et renvoyé directement.
Différents Hook memoizedState enregistrent différents contenus. useState
enregistre les informations state
, useEffect
enregistre les objets effect
, useRef
enregistre les objets ref
...
mountWorkInProgressHook
et updateWorkInProgressHook
sont soutenues par une liste chaînée de Hooks. Lorsque la liste chaînée n'est pas modifiée, vous pouvez ensuite. récupérez le même objet memoizedState à chaque fois que vous effectuez un rendu useRef. C'est aussi simple que cela.
À ce stade, nous comprenons la logique de transmission et d'attribution ref
dans React, ainsi que le code source lié à useRef
. Utilisez une question d'application pour consolider les points de connaissances ci-dessus : Il y a un composant Input à l'intérieur du composant, innerRef HTMLInputElement
doit être utilisé pour accéder au nœud DOM
. En même temps, il permet également au composant externe de référencer le nœud. pour le mettre en œuvre ?
const Entrée = forwardRef((props, ref) => { const innerRef = useRef<HTMLInputElement>(null) retour ( <entrée {...props} ref={???} /> ) })
Considérez comment ???
dans le code ci-dessus doit être écrit.
============ Ligne de démarcation de réponse ==============
En comprenant l'implémentation interne liée à Ref, il est évident que nous pouvons créer ici un RefCallback
, qui peut gérer plusieurs Attribuez simplement une ref
.
fonction d'exportation combineRefs<T = any>( refs : Array<MutableRefObject<T | null> | RefCallback<T>> ): React.RefCallback<T> { valeur de retour => { refs.forEach(ref => { if (typeof ref === 'fonction') { ref(valeur); } sinon if (ref !== null) { ref.current = valeur ; } }); } ; } const Entrée = forwardRef((props, ref) => { const innerRef = useRef<HTMLInputElement>(null) retour ( <input {...props} ref={combineRefs(ref, innerRef)} /> ) })