Qual é o proptype correto para um ref em React?

87

Estou armazenando um ref em minha loja redux e usando mapStateToProps para expor o ref para componentes que precisam de acesso a ele.

A referência armazenada é semelhante a:

ref={node => this.myRefToBePutInReduxGlobalStore = node}

Qual é o propType correto para este ref?

danday74
fonte
8
Como os refs não são serializáveis, não é uma boa ideia colocá-los na loja Redux. No entanto, o valor passado para refprop é uma função com o primeiro argumento uma referência ao elemento DOM ou nulo. É uma função .
rishat
5
Você não pode usar propType para refs, mas pode usá-lo para props. Já que você o está passando para a redux store, ele é representado como um proplike this.props.myRefToBePutInReduxGlobalStore. myRefToBePutInReduxGlobalStoredeve ser o tipo de nó, então PropTypes.nodedeve funcionar para você
The Reason
Acho que deveria ser PropType.objectporque ref é basicamente uma instância de classe que é um objeto. Portanto, quando você passa o elemento ref como um adereço, ele seria do tipo de objeto
Hriday Modi

Respostas:

99

TL; DR

Se você deseja prop tipo um ref que espera apenas elementos DOM nativos, como um divou um input, a definição correta é a seguinte:

refProp: PropTypes.oneOfType([
    // Either a function
    PropTypes.func, 
    // Or the instance of a DOM native element (see the note about SSR)
    PropTypes.shape({ current: PropTypes.instanceOf(Element) })
])

Resposta ao problema específico descrito na postagem original

No exemplo da pergunta do OP, não é o tipo ref prop que precisa ser declarado, mas o material apontado pelo ref, e que será passado do redux usando mapStateToProps. O tipo de prop para um elemento DOM seria: myRefToBePutInReduxGlobalStore: PropTypes.instanceOf(Element)(se for um elemento DOM). Embora, eu:

  1. Renomeie-o para myElementToBePutInReduxGlobalStore(um elemento não é uma referência)
  2. Evite armazenar dados não serializáveis ​​dentro do redux store
  3. Evite passar elementos em adereços, conforme explicado pelo engenheiro do React Seb Markbage

Resposta longa para a pergunta real

P: Qual é o proptype correto para um ref no React?

Exemplo de caso de uso:

function FancyInput({ inputRef }) {
    return (
        <div className="fancy">
            Fancy input
            <input ref={inputRef} />
        </div>
    )
}

FancyInput.propTypes = {
    inputRef: ???   // What is the correct prop type here?
}

function App(props) {
    const inputRef = React.useRef()
    useLayoutEffect(function focusWhenStuffChange() {
        inputRef.current?.focus()
    }, [props.stuff])
    return <FancyInput inputRef={inputRef} />
}

Hoje, existem dois tipos de árbitros em reação:

  1. Um objeto que se parece com este :, { current: [something] }geralmente criado por React.createRef()auxiliar ou React.useRef()gancho
  2. Uma função de retorno de chamada que receberá [something]como argumento (como a do exemplo OP)

Observação: historicamente, você também pode usar uma string ref, mas ela é considerada legada e será removida do react

O segundo item é bastante simples e requer o seguinte tipo de prop: PropTypes.func.

A primeira opção é menos óbvia porque você pode querer especificar o tipo de elemento apontado pelo ref.

Muitas boas respostas

ref pode apontar para algo diferente do elemento DOM.

Se você quiser ser totalmente flexível:

refProp: PropTypes.oneOfType([
    PropTypes.func, 
    PropTypes.shape({ current: PropTypes.any })
])

O acima apenas reforça a forma do objeto ref w / currentproperty. Funcionará sempre para qualquer tipo de ref. Para o propósito de usar o tipo de objeto, que é uma maneira de detectar quaisquer inconsistências durante o desenvolvimento , isso provavelmente é o suficiente. Parece muito improvável que um objeto com forma { current: [any] }e seja passado para umrefToForward suporte, não seja um verdadeiro ref.

No entanto , você pode querer declarar que seu componente não espera nenhum tipo de ref, mas apenas um certo tipo, dado o que ele precisa para esse ref.

Eu configurei um sandbox mostrando algumas maneiras diferentes de declarar um ref, mesmo algumas não convencionais, e então testando-as com muitos tipos de adereços. Você pode encontrá-lo aqui .


Se você espera apenas um ref apontando para um elemento de entrada nativo, não para qualquer HTML Element:

refProp: PropTypes.oneOfType([
    PropTypes.func, 
    PropTypes.shape({ current: PropTypes.instanceOf(HTMLInputElement) })
])

Se você espera apenas um ref apontando para um componente da classe React:

refProp: PropTypes.oneOfType([
    PropTypes.func, 
    PropTypes.shape({ current: PropTypes.instanceOf(Component) })
])

Se você espera apenas um ref apontando para um componente funcional usando useImperativeHandlepara expor algum método público:

refProp: PropTypes.oneOfType([
    PropTypes.func, 
    PropTypes.shape({ current: PropTypes.object })
])

Observação: o tipo de prop acima é interessante porque também cobre componentes de reação e elementos DOM nativos, porque todos eles herdam o javascript base Object


Não existe uma maneira única de declarar o tipo de suporte para um ref, depende do uso. Se o seu árbitro apontar para algo muito identificado, pode valer a pena adicionar um tipo específico de suporte. Caso contrário, apenas verificar a forma geral parece suficiente.


Nota sobre a renderização do lado do servidor

Se o seu código tiver que ser executado no servidor, a menos que você já tenha um ambiente DOM do polyfill, Elementou qualquer outro tipo do lado do cliente estará undefinedem NodeJS. Você pode usar o seguinte calço para apoiá-lo:

Element = typeof Element === 'undefined' ? function(){} : Element

As seguintes páginas do React Docs fornecem mais informações sobre como usar refs, objetos e callbacks , e também como usar o useRefgancho

Resposta atualizada agradecendo a @Rahul Sagore, @Ferenk Kamra, @svnm e @Kamuela Franco

Pandaiolo
fonte
2
Ele Elementnão está definido na renderização do lado do servidor. Alguma solução para isso?
Rahul Sagore
Bom ponto. E quanto a este shim: Element = typeof Element === 'undefined' ? function(){} : Element(sugerido por Ryan Florence em alguma edição do github). Se funcionar para você, posso adicioná-lo à minha resposta.
Pandaiolo
Eu descobri e adicionei isso if (!isBrowser) { global.Element = null; }. Funcionou para mim. isBrowser = typeof window !== 'undefined';
Rahul Sagore
Ótima resposta. Eu adoraria que o TL; DR estivesse no topo da resposta.
Kamuela Franco
12

Muito semelhante à postagem de @Pandaiolo,

PropTypes.elementType agora foi adicionado

forwardedRef: PropTypes.oneOfType([
  PropTypes.func,
  PropTypes.shape({ current: PropTypes.elementType })
]),

Se estiver usando PropTypes> = 15.7.0 PropTypes.elementTypefoi adicionado em 10 de fevereiro de 2019 neste PR

svnm
fonte
Obrigado @svnm, atualizei minha resposta para refletir isso, o que a torna mais simples!
Pandaiolo
1
No final, isso elementTypenão funcionou para mim, então, na verdade, testei vários tipos de adereços em diferentes tipos de componentes que podem ser apontados por um ref, em uma sandbox. Aqui estão os resultados: codesandbox.io/s/loving-benz-w6fbh . Não tenho certeza se cobri todas as possibilidades, mas parece que o proptype correto para o elemento DOM é instanceOf(Element)(ou object), para uma classe é instanceOf(Component)(ou object) e para o caso de uso específico de um componente funcional usandouseImperativeHandle é object. Pensamentos?
Pandaiolo
9

Eu apenas verifico a forma preferida e como PropType.object não é muito valioso, acabei usando este:

PropTypes.shape({ current: PropTypes.instanceOf(Element) })
Ferenc Kamras
fonte
1

Eu verifiquei todas as respostas acima, mas recebo um aviso da reação, então pesquisei muito e achei a resposta certa.

import React, { Component } from 'react';

ComponentName.propTypes = {
  reference: PropTypes.oneOfType([
    PropTypes.func,
    PropTypes.shape({ current: PropTypes.instanceOf(Component) }),
  ]),
};
Nisharg Shah
fonte