Next.js: Próximo 9 - Usando componentes funcionais como filho de<link/> causa avisos de referência

Criado em 12 jul. 2019  ·  32Comentários  ·  Fonte: vercel/next.js

Relatório de erro

Descreva o bug

Usar um componente funcional como filho de <Link/> causa:

Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?

Isso aconteceu apenas após a atualização da v8 => v9.

Reproduzir

Repo de reprodução: https://github.com/nickluger/nextjs-link-ref-functional-comp-warning

Comportamento esperado

<Link/> deve funcionar com componentes funcionais, também / não mostrar um aviso.

Informação do sistema

  • Versão do Next.js: 9.0.1
bug

Comentários muito úteis

Encapsular todos os componentes funcionais usados ​​em Link manualmente ? Não deveria haver uma solução mais fácil e menos repetitiva para isso? Pelo menos algo como <Link href="/" forwardRef>...</Link> ?

Todos 32 comentários

O mesmo aqui. Estou tendo um erro se usar um componente personalizado como este:

        <Link href="/href">
          <Button
            type="custom"
            color="#FCFCFC"
          >
             buttonText
          </Button>
        </Link>

Isso só acontece quando você usa um componente como filho. Eles não fornecem um aviso, por exemplo:

        <Link href="/href">
          <button
            type="custom"
            color="#FCFCFC"
          >
            buttonText
          </button>
        </Link>

        <Link href="/href">
          <a>
            <Button
              type="custom"
              color="#FCFCFC"
            >
              buttonText
            </Button>
          </a>
        </Link>

Existe uma maneira de contornar esse bug? estamos impedidos de ir para 9 para este, quando alguns recursos que realmente gostaríamos de aproveitar, como o suporte de digitação. Acabei de notar que atingiu 9.0.3: /

Você pode tentar silenciar temporariamente, como:

// TODO: Muting error, fix as soon as zeit/next.js/issues/7915 resolved
const originalError = console.error;

console.error = (...args) => {
  if (/Warning.*Function components cannot be given refs/.test(args[0])) {
    return;
  }
  originalError.call(console, ...args);
};

Estou tendo o mesmo problema aqui.

Se você usar <Link/> para embrulhar seu componente personalizado, poderá encaminhar ref assim:

const CustomComponent = React.forwardRef(function CustomComponent(props, ref) {
  return (
    <div/>
  );
});
<Link href={"/"}>
  <CustomComponent/>
</Link>

O aviso vai embora.

Eu atualizei para 9.0.4-canary.2 mas ainda recebo o aviso no console. Estou esquecendo de algo?

Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?

Check the render method of `Link`.

@ th0th Você deve envolver seu componente em React.forwardRef . Como este:

import React from 'react'
import Link from 'next/link'

const CustomComponent = React.forwardRef((props, ref) => (
  <a ref={ref} {...props}>
    Click
  </a>
))

export default () => (
  <Link href='/' passHref>
    <CustomComponent/>
  </Link>
)

Encapsular todos os componentes funcionais usados ​​em Link manualmente ? Não deveria haver uma solução mais fácil e menos repetitiva para isso? Pelo menos algo como <Link href="/" forwardRef>...</Link> ?

Concorde com @nickluger, isso parece contra-intuitivo.

Todas as outras bibliotecas devem implementar forwardRef attribute da mesma maneira? Ou talvez os criadores da reação pensassem que os desenvolvedores usariam a função React.forwardRef vez de implementar o atributo forwardRef ?

Sim, concordo com @nickluger , é uma dor fazer isso manualmente para todos os componentes dentro de um Link .

Devemos reabrir e renomear este problema (@ijjk) ou alguém já abriu um novo?

O mesmo aqui. Estou tendo um erro se usar um componente personalizado como este:

        <Link href="/href">
          <Button
            type="custom"
            color="#FCFCFC"
          >
             buttonText
          </Button>
        </Link>

Isso só acontece quando você usa um componente como filho. Eles não fornecem um aviso, por exemplo:

        <Link href="/href">
          <button
            type="custom"
            color="#FCFCFC"
          >
            buttonText
          </button>
        </Link>

        <Link href="/href">
          <a>
            <Button
              type="custom"
              color="#FCFCFC"
            >
              buttonText
            </Button>
          </a>
        </Link>

Isso provavelmente gerará um erro de console:

Warning: validateDOMNesting(...): <a> cannot appear as a descendant of <a>.

Acabei de encontrar esse problema ao atualizar o Next JS. Existe uma correção no caminho?

Não tenho certeza se entendo como usar forwardRef e envolver meu componente. Meu arquivo button.js exporta atualmente o seguinte ....

const Button = (props) => {
    return (<stuff />)
}

export default React.memo(Button)

Veja acima, abri um follow-up aqui: https://github.com/zeit/next.js/issues/8962

Acabei de encontrar esse problema ao atualizar o Next JS. Existe uma correção no caminho?

Não tenho certeza se entendo como usar forwardRef e envolver meu componente. Meu arquivo button.js exporta atualmente o seguinte ....

const Button = (props) => {
    return (<stuff />)
}

export default React.memo(Button)

Neste caso, você deve ser capaz de apenas fazer

import {forwardRef} from 'react';
const Button = forwardRef(props, ref) => {
    return (<stuff ref={ref} />)
}

para a parte forwardRef. Os documentos também mencionam https://nextjs.org/docs#forcing -the-link-to-expose-href-to-its-child, embora eu não tenha tentado isso. Eu descobri que fazendo isso

            <Link
              href={href}
            >
              <a>
                {text}
              </a>
            </Link>

funciona (apesar de parecer muito estranho.)

Encontrar este problema no 9.0.2

Usar <a></a> ao redor pode funcionar, mas irá disparar outro erro como "deve ter href", etc. ao usar a11y e semelhantes.

Edit: Gastei 15 minutos tentando descobrir isso, é muito chato, uma grande perda de tempo, e parece se comportar corretamente, apesar do aviso.

Pessoalmente, acessar https://github.com/zeit/next.js/issues/7915#issuecomment -511792629 é muito mais fácil.

Que tal essa solução usando um fragmento de reação como um invólucro de rolamento?

import React, { Fragment, useRef } from 'react';
import { MyFunctionalComponent } from './my-functional-component';

const App = props => {
  const myRef = useRef();

  return ( 
    <Link href={href}>
      <Fragment>
          <MyFunctionalComponent ref={myRef} />
      </Fragment>
    </Link>
  );
}

Onde <MyFunctionalComponent> encaminha a referência:

import React, { forwardRef } from 'react';

const MyFunctionalComponent = forwardRef(props, ref) => (
  <div ref={ref} />
);

Isso não adiciona nenhum wrapper HTML adicional na saída ( <div /> ) e não deve acionar nenhum erro relacionado.

@ hill84 Interessante, mas de onde myRef vem em seu exemplo?

@Vadorequest Eu adicionei mais detalhes ao meu comentário anterior.

Seguir a sugestão de @ hill84 foi uma boa solução alternativa:
`` `
const ButtonLink: FC= ({crianças, href, ... adereços}) => (
<

Seguir a sugestão de @ hill84 foi uma boa solução alternativa:

const ButtonLink: FC<ButtonLinkProps> = ({ children, href, ...props }) => (
  <Link href={href}>
    <><Button link {...props}>{children}</Button></>
  </Link>
);

Eu uso <><Component/></> mas o Link não pode agir. Tenho que usar <div></div> vez de.

Eu sugiro uma solução em que uma função retorna um objeto com adereços em vez de um componente:

const propsUserBtn = user => ({
    className: "h-10 w-10 font-black rounded-full bg-orange-200 text-teal-700",
    title: user.first_name + ' ' + user.last_name,
    children: (user.first_name.charAt(0) + user.last_name.charAt(0)).toUpperCase()
})
<Link href='/profile'>
    <button { ...propsUserBtn(user) } />   
</Link>

Verificado, funciona!

Por que este ref é mesmo necessário? É para garantir o posicionamento correto de href no componente empacotado subjacente quando passHref é usado em <Link /> ?

Isso é muito irritante, pois meu

Ou, pelo menos, que seja um recurso opcional se você precisar especificar a colocação de href em seu componente. <Link href="/" passHref useRef /> ?

Não acho que ter que criar outro módulo para encaminhar o ref para um componente personalizado seja uma boa solução e também seja muito repetitivo.

A maneira que estou usando para lidar com esse problema é envolvendo o componente personalizado com um elemento div , como sugerido por @ namlq93, mas também não parece estar limpo ...

O mesmo aqui. Estou tendo um erro se usar um componente personalizado como este:

        <Link href="/href">
          <Button
            type="custom"
            color="#FCFCFC"
          >
             buttonText
          </Button>
        </Link>

Isso só acontece quando você usa um componente como filho. Eles não fornecem um aviso, por exemplo:

        <Link href="/href">
          <button
            type="custom"
            color="#FCFCFC"
          >
            buttonText
          </button>
        </Link>

        <Link href="/href">
          <a>
            <Button
              type="custom"
              color="#FCFCFC"
            >
              buttonText
            </Button>
          </a>
        </Link>

Funciona, obrigado amigo !!

Estou tendo um problema semelhante

ainda lá

https://nextjs.org/docs/api-reference/next/link#if -the-child-is-a-function-component

Isso é triste.

Eu estava assistindo a conferência NextJS outro dia pensando "Wow Next é o futuro". Eu realmente gosto do Next e do ecossistema Vercel e quero honestamente poder dizer isso e ser um defensor do Next. No entanto, questões como essas sendo fechadas e não tratadas tornam isso difícil. Já vi esse tipo de padrão várias vezes em todos os problemas aqui, especificamente com o Roteador.

Existem 3 soluções para este problema do lado do usuário e nenhuma delas é boa DX.

  1. A forma recomendada React.forwardRef. envolva o componente em React.forwardRef . Isso pode ser muito repetitivo
  2. Envolva o componente funcional em uma tag como div , span , a , etc.
  3. Em vez de Link, coloque useRouter e use router.push

Não importa qual solução você use, eles são todos estranhos e requerem comentários com links para este problema ou os documentos para explicar aos colegas de trabalho / contribuidores por que é necessário evitar a remoção / limpeza acidental.

Acho que esse assunto deve ser reaberto para discussão.

Esta página foi útil?
0 / 5 - 0 avaliações