Next.js: Suivant 9 - Utilisation de composants fonctionnels comme enfants de<link/> provoque des avertissements de référence

Créé le 12 juil. 2019  ·  32Commentaires  ·  Source: vercel/next.js

Rapport d'erreur

Décrivez le bogue

L'utilisation d'un composant fonctionnel comme enfant de <Link/> provoque:

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

Cela ne s'est produit qu'après la mise à niveau de v8 => v9.

Reproduire

Repo de reproduction: https://github.com/nickluger/nextjs-link-ref-functional-comp-warning

Comportement prévisible

<Link/> devrait également fonctionner avec les composants fonctionnels / ne pas afficher d'avertissement.

Informations système

  • Version de Next.js: 9.0.1
bug

Commentaire le plus utile

Enveloppez manuellement tous les composants fonctionnels utilisés dans un Link ? Ne devrait-il pas y avoir une solution plus simple et moins répétitive pour cela? Au moins quelque chose comme <Link href="/" forwardRef>...</Link> ?

Tous les 32 commentaires

Pareil ici. J'ai une erreur si j'utilise un composant personnalisé comme celui-ci:

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

Cela ne se produit que lorsque vous utilisez un composant en tant qu'enfant. Ceux-ci ne donnent pas d'avertissement par exemple:

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

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

Y a-t-il un moyen de contourner ce bug? nous ne pouvons pas passer à 9 pour celui-ci lorsque certaines fonctionnalités que nous aimerions vraiment exploiter comme le support dactylographié. Je viens de remarquer qu'il est passé à 9.0.3: /

Vous pouvez essayer de le désactiver temporairement, comme:

// 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);
};

J'ai le même problème ici.

Si vous utilisez <Link/> pour envelopper votre composant personnalisé, vous pouvez transférer ref comme ceci:

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

L'avertissement disparaît.

J'ai mis à jour vers 9.0.4-canary.2 mais je reçois toujours l'avertissement sur la console. Est-ce que je manque quelque chose?

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 Vous devez envelopper votre composant dans React.forwardRef . Comme celui-ci:

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>
)

Enveloppez manuellement tous les composants fonctionnels utilisés dans un Link ? Ne devrait-il pas y avoir une solution plus simple et moins répétitive pour cela? Au moins quelque chose comme <Link href="/" forwardRef>...</Link> ?

D'accord avec @nickluger, cela semble contre-intuitif.

Toutes les autres bibliothèques devraient-elles implémenter l'attribut forwardRef de la même manière? Ou peut-être que les créateurs de React ont pensé que les développeurs utiliseraient la fonction React.forwardRef au lieu d'implémenter l'attribut forwardRef ?

Oui d'accord avec @nickluger , c'est Link .

Devrions-nous rouvrir et renommer ce numéro (@ijjk) ou est-ce que quelqu'un en a déjà ouvert un nouveau?

Pareil ici. J'ai une erreur si j'utilise un composant personnalisé comme celui-ci:

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

Cela ne se produit que lorsque vous utilisez un composant en tant qu'enfant. Ceux-ci ne donnent pas d'avertissement par exemple:

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

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

Cela générera très probablement une erreur de console:

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

Je viens de rencontrer ce problème lors de la mise à niveau de Next JS. Y a-t-il une solution en cours?

Je ne suis pas sûr de comprendre comment utiliser forwardRef et encapsuler mon composant. Mon fichier button.js exporte actuellement les éléments suivants ...

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

export default React.memo(Button)

Voir ci-dessus, j'ai ouvert un suivi ici: https://github.com/zeit/next.js/issues/8962

Je viens de rencontrer ce problème lors de la mise à niveau de Next JS. Y a-t-il une solution en cours?

Je ne suis pas sûr de comprendre comment utiliser forwardRef et encapsuler mon composant. Mon fichier button.js exporte actuellement les éléments suivants ...

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

export default React.memo(Button)

Dans ce cas, vous devriez pouvoir simplement faire

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

pour la partie forwardRef. La documentation mentionne également https://nextjs.org/docs#forcing -the-link-to-expose-href-to-its-child, bien que je n'ai pas essayé cela. J'ai trouvé ça en faisant ça

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

fonctionne (bien que cela semble très étrange.)

Rencontrer ce problème dans la version 9.0.2

L'utilisation de <a></a> around peut fonctionner, mais déclenchera d'autres erreurs comme "must have href", etc. lors de l'utilisation de a11y et similaire.

Edit: J'ai passé 15 minutes à essayer de comprendre cela, c'est très ennuyeux, une telle perte de temps, et semble se comporter correctement malgré l'avertissement.

Personnellement, aller pour https://github.com/zeit/next.js/issues/7915#issuecomment -511792629 beaucoup plus facile.

Qu'en est-il de cette solution utilisant un fragment de réaction comme enveloppe de roulement?

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>
  );
}

<MyFunctionalComponent> transfère la référence:

import React, { forwardRef } from 'react';

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

Cela n'ajoute aucun wrapper HTML supplémentaire dans la sortie ( <div /> ) et ne devrait pas déclencher d'erreur connexe.

@ hill84 Intéressant, mais d'où vient myRef dans votre exemple?

@Vadorequest J'ai ajouté plus de détails à mon commentaire précédent.

Suivre la suggestion de @ hill84 était une bonne solution:
''
Const ButtonLink: FC= ({enfants, href, ... accessoires}) => (
<

Suivre la suggestion de @ hill84 était une bonne solution:

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

J'utilise <><Component/></> mais Link ne peut pas agir. Je dois utiliser <div></div> au lieu de.

Je suggère une solution où une fonction renvoie un objet avec des accessoires au lieu d'un composant:

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>

Vérifié, ça marche!

Pourquoi cette référence est-elle même nécessaire? Est-ce pour garantir le placement correct du href dans le composant encapsulé sous-jacent lorsque passHref est utilisé dans le <Link /> ?

C'est super ennuyeux car mon

Ou au moins, faites-en une fonctionnalité optionnelle si vous devez spécifier l'emplacement du href dans votre composant. <Link href="/" passHref useRef /> ?

Je ne pense pas que devoir créer un autre module pour transmettre la référence à un composant personnalisé soit une bonne solution et aussi trop répétitif.

La façon dont j'utilise pour gérer ce problème consiste à envelopper le composant personnalisé avec un élément div comme suggéré par @ namlq93, mais il ne semble pas non plus propre ...

Pareil ici. J'ai une erreur si j'utilise un composant personnalisé comme celui-ci:

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

Cela ne se produit que lorsque vous utilisez un composant en tant qu'enfant. Ceux-ci ne donnent pas d'avertissement par exemple:

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

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

Cela fonctionne, merci mon pote !!

J'ai un problème similaire

toujours là

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

C'est triste.

Je regardais la conférence NextJS l'autre jour en pensant "Wow Next is the future". J'aime beaucoup Next et l'écosystème Vercel et je veux honnêtement pouvoir le dire et être un défenseur de Next. Cependant, des problèmes comme ceux-ci étant clos et non résolus, il est difficile de le faire. J'ai vu ce type de modèle plusieurs fois tout au long des problèmes ici, en particulier avec le routeur.

Il existe 3 solutions à ce problème du côté utilisateur et aucune d'entre elles n'est excellente DX.

  1. La méthode recommandée React.forwardRef. envelopper le composant dans React.forwardRef . Cela peut être très répétitif
  2. Enveloppez le composant fonctionnel dans une balise comme div , span , a , etc.
  3. Au lieu de Link, tirez useRouter et utilisez router.push

Quelle que soit la solution que vous utilisez, elles sont toutes étranges et nécessitent des commentaires liés à ce problème ou à la documentation pour expliquer aux collègues / contributeurs pourquoi cela est nécessaire pour éviter toute suppression / nettoyage accidentel.

Je pense que cette question devrait être rouverte pour discussion.

Cette page vous a été utile?
0 / 5 - 0 notes