Como começar rapidamente com VUE3.0: Entre e aprenda
Em projetos React, há muitos cenários onde Ref
é necessário. Por exemplo, use ref
para obter o nó DOM e obter a instância do objeto ClassComponent, use o gancho useRef
para criar um objeto Ref para React.createRef
o problema de setInterval
não ser capaz de obter o estado mais recente; método para criar manualmente um objeto Ref
.
Embora Ref
seja muito simples de usar, ainda é inevitável encontrar problemas em projetos reais. Este artigo resolverá vários problemas relacionados Ref
do ponto de vista do código-fonte e esclarecerá o que é feito por trás ref
. Depois de ler este artigo, você poderá ter uma compreensão mais profunda da Ref
.
Em primeiro lugar, ref
é a abreviatura de reference
, que é uma referência. No arquivo de declaração de tipo de react
, você pode encontrar vários tipos relacionados a Ref, e eles estão listados aqui.
RefObject<T> { somente leitura atual: T | interface MutableRefObject<T> { current: T }
RefObject
{ current: T }
useRef
Hook, RefObject /MutableRefObejct é retornado. , o Typescript avisará ⚠️ se refObject.current
for modificado.
const ref = useRef<string>(nulo) ref.current = '' // Erro
TS: Não pode ser atribuído a "atual" porque é uma propriedade somente leitura.
Veja a definição do método useRef
. A sobrecarga de função é usada aqui. Quando o parâmetro null
de entrada T
não contém null
, MutableRefObject<T>
RefObject<T>
retornado.
função useRef<T>(ValorInicial: T): MutableRefObject<T>; function useRef<T>(initialValue: T | null): RefObject<T>
Portanto, se você deseja que a propriedade atual do objeto ref criado seja modificável, você precisa adicionar | null
;
const ref = useRef<string | nulo>(nulo) ref.current = '' // OK,
ao chamar o método React.createRef()
, um RefObject
também é retornado.
createRef
createRef(): RefObject { const refObject = { atual: nulo, }; se (__DEV__) { Object.seal(refObject); } retornar refObject; }
RefObject/MutableRefObject
foi adicionado na versão 16.3
. Se você usar versões anteriores, precisará usar Ref Callback
.
Usar Ref Callback
é passar uma função de retorno de chamada. Quando o react chama de volta, a instância correspondente será passada de volta e pode ser salva por si só para chamada. O tipo desta função de retorno de chamada é RefCallback
.
type RefCallback<T> = (instância: T | null) => void
Exemplo de uso de RefCallback
:
import React from 'react';
classe de exportação CustomTextInput estende React.Component { textInput: HTMLInputElement | nulo = nulo; saveInputRef = (elemento: HTMLInputElement | null) => { this.textInput = elemento; } renderizar() { retornar ( <input type="text" ref={this.saveInputRef} /> ); } }
Na declaração de tipo, também existem tipos Ref/LegacyRef, que são usados para se referir ao tipo Ref em geral. LegacyRef
é uma versão compatível Na versão anterior, ref
também poderia ser uma string.
tipo Ref<T> = RefCallback<T> | type LegacyRef<T> = string | Ref<T>;
Somente entendendo os tipos relacionados a Ref você poderá se sentir mais confortável escrevendo Typescript.
PassandoAo usar ref
em um componente JSX, definimos um Ref
para o atributo ref
. Todos sabemos que a sintaxe do jsx
será compilada na forma createElement
por ferramentas como Babel.
//jsx <App ref={ref} id="meu-app" </App> //compilado para React.createElement(Aplicativo, { referência: referência, id: "meu-aplicativo" });
Parece que ref
não é diferente de outros adereços, mas se você tentar imprimir props.ref dentro do componente, ele será undefined
. E o console do ambiente dev
fornecerá avisos.
Tentar acessá-lo resultará no retorno de
undefined
. Se você precisar acessar o mesmo valor dentro do componente filho, você deve passá-lo como um suporte diferente
. Como você pode ver no código fonte do ReactElement, ref
é RESERVED_PROPS
key
também terão esse tratamento. Elas serão especialmente processadas e extraídas de props e passadas para Element
.
const RESERVED_PROPS = { chave: verdadeiro, referência: verdadeiro, __self: verdadeiro, __fonte: verdadeiro, };
Então ref
é “props“
que será tratado de maneira especial.
Antes da versão 16.8.0
, o Function Component não tinha estado e só seria renderizado com base nos adereços recebidos. Com o Hook, você pode não apenas ter estado interno, mas também expor métodos para chamadas externas (exigindo forwardRef
e useImperativeHandle
).
Se você usar ref
diretamente para um Function Component
, o console no ambiente de desenvolvimento avisará que você precisa envolvê-lo com forwardRef
.
functionInput() { retornar <entrada /> } const ref=useRef() <Input ref={ref} />
Os componentes da função não podem receber referências. As tentativas de acessar esta referência falharão
forwardRef
Veja o código fonte ReactForwardRef.js Dobre o código relacionado __DEV__
É apenas um componente de alta ordem extremamente simples. Receba um FunctionComponent renderizado, envolva-o e defina $$typeof
como REACT_FORWARD_REF_TYPE
e return
-o.
Rastreie o código e encontre resolveLazyComponentTag, onde $$typeof
será analisado na WorkTag correspondente.
A WorkTag correspondente a REACT_FORWARD_REF_TYPE
é ForwardRef. Então ForwardRef entrará na lógica de updateForwardRef.
caso ForwardRef: { filho = updateForwardRef( nulo, trabalho em andamento, Componente, resolvidoProps, renderizar pistas, ); criança devolvida; }
Este método chamará o método renderWithHooks e passará ref
no quinto parâmetro.
nextChildren = renderWithHooks( atual, trabalho em andamento, renderizar, próximoAdereços, ref, // aqui renderLanes, );
Continue a rastrear o código e insira o método renderWithHooks. Você pode ver que ref
será passado como o segundo parâmetro de Component
. Neste ponto podemos entender de onde vem o segundo parâmetro ref
de FuncitonComponent
envolvido por forwardRef
(em comparação com o segundo parâmetro do construtor ClassComponent que é Context).
Sabendo como passar ref, a próxima questão é como o ref é atribuído.
O(atribuir um RefCallback a ref e quebrar o ponto no retorno de chamada). Rastreie o código commitAttachRef. Neste método, será julgado se o ref do nó Fiber é function
ou RefObject e a instância. será processado de acordo com o tipo. Se o nó Fiber for um HostComponent ( tag = 5
), que é um nó DOM, instance é o nó DOM e se o nó Fiber for um ClassComponent ( tag = 1
), instance é a instância do objeto;
function commitAttachRef(trabalhoacabado) { var ref = terminouWork.ref; if (ref! == nulo) { var instanceToUse = terminouWork.stateNode; if (typeof ref === 'função') { ref(instanceToUse); } outro { ref.atual = instânciaToUse; } } }
O exemplo acima é a lógica de atribuição de ref em HostComponent e ClassComponent. Para componentes do tipo ForwardRef, códigos diferentes são usados, mas o comportamento é basicamente o mesmo. Você pode ver o imperativoHandleEffect aqui.
A seguir, continuamos a nos aprofundar no código-fonte do React para ver como useRef é implementado.
localiza o código de tempo de execução useRef ReactFiberHooks rastreando o código
Existem dois métodos aqui, mountRef
e updateRef
, como o nome sugere, eles correspondem às operações em ref
quando Fiber
mount
e update
.
função updateRef<T>(ValorInicial: T): {|atual: T|} { const gancho=updateWorkInProgressHook(); retornar hook.memoizedState; } função mountRef<T>(ValorInicial: T): {|atual: T|} { const gancho=mountWorkInProgressHook(); const ref = {atual: valorinicial}; gancho.memoizedState = ref; retornar referência; }
Você pode ver que quando mount
, useRef
cria um RefObject
e o atribui ao memoizedState
do hook
. Ao update
, ele é retirado e retornado diretamente.
Diferente Hook memoizedState salva conteúdos diferentes. useState
salva informações state
, useEffect
salva objetos effect
, useRef
salva objetos ref
...
mountWorkInProgressHook
e updateWorkInProgressHook
são apoiados por uma lista vinculada de Hooks. recupere o mesmo objeto memoizedState toda vez que você renderizar useRef. É simples assim.
Neste ponto, entendemos a lógica de passar e atribuir ref
no React, bem como o código-fonte relacionado a useRef
. Use uma pergunta de aplicativo para consolidar os pontos de conhecimento acima: Há um componente de entrada dentro do componente, innerRef HTMLInputElement
precisa ser usado para acessar o nó DOM
. Ao mesmo tempo, também permite que o componente externo refira o nó. para implementá-lo?
const Entrada = forwardRef((props, ref) => { const innerRef = useRef<HTMLInputElement>(null) retornar ( <input {...props} ref={???} /> ) })
Considere como ???
no código acima deve ser escrito.
============ Linha divisória da resposta ==============
Ao entender a implementação interna relacionada ao Ref, é óbvio que podemos criar um RefCallback
aqui, que pode lidar com vários Basta atribuir um ref
.
função de exportação combineRefs<T = any>( refs: Array<MutableRefObject<T | null> | RefCallback<T>> ): React.RefCallback<T> { valor de retorno => { refs.forEach(ref => { if (typeof ref === 'função') { ref(valor); } else if (ref! == nulo) { ref.atual = valor; } }); }; } const Entrada = forwardRef((props, ref) => { const innerRef = useRef<HTMLInputElement>(nulo) retornar ( <input {...props} ref={combineRefs(ref, innerRef)} /> ) })