React: createPortal : option de support pour arrêter la propagation des événements dans l'arborescence React

Créé le 27 oct. 2017  ·  103Commentaires  ·  Source: facebook/react

Vous souhaitez demander une fonctionnalité ou signaler un bug ?
Fonctionnalité, mais aussi un bug car la nouvelle API casse l'ancienne unstable_rendersubtreeintocontainer

Quel est le comportement actuel ?
Nous ne pouvons pas arrêter la propagation de tous les événements du portail vers ses ancêtres de l'arbre React. Notre mécanisme de couches avec les modaux/popovers est complètement cassé. Par exemple, nous avons un bouton déroulant. Lorsque nous cliquons dessus, cliquez sur ouvre popover. Nous souhaitons également fermer cette fenêtre contextuelle en cliquant sur le même bouton. Avec createPortal, cliquez à l'intérieur du popover, cliquez sur le bouton et il se ferme. Nous pouvons utiliser stopPropagation dans ce cas simple. Mais nous avons des tonnes de tels cas, et nous devons utiliser stopPropagation pour tous. De plus, nous ne pouvons pas arrêter tous les événements.

Quel est le comportement attendu ?
createPortal devrait avoir une option pour arrêter la propagation des événements synthétiques via l'arborescence React sans arrêter manuellement chaque événement. Qu'est-ce que tu penses?

DOM Feature Request

Commentaire le plus utile

Même cela me semble inutilement complexe. Pourquoi ne pas simplement ajouter un indicateur booléen facultatif à createPortal permettant de bloquer le comportement de bullage ?

Tous les 103 commentaires

De plus, la propagation de mouseOver/Leave semble complètement inattendue.
image

Pouvez-vous déplacer le portail en dehors du bouton ?

par exemple

return [
  <div key="main">
    <p>Hello! This is first step.</p>
    <Button key="button" />
  </div>,
  <Portal key="portal" />
];

Ensuite, il ne fera pas de bulles à travers le bouton.

C'était ma première pensée, mais !) Imaginez que nous ayons le gestionnaire mouseEnter dans un tel conteneur de composants :

image

Avec unstable_rendersubtreeintocontainer je n'ai rien à voir avec les événements dans le composant ButtonWithPopover - mouseEnter fonctionne simplement lorsque la souris entre vraiment div et l'élément DOM du bouton, et n'est pas déclenché lorsque la souris est sur popover. Avec le portail, l'événement se déclenche lorsque la souris survole le popover - et en fait PAS au-dessus de div en ce moment. Donc, je dois arrêter la propagation. Si je le fais dans le composant ButtonWithPopover , j'interromprai le déclenchement de l'événement lorsque la souris sera sur le bouton. Si je le fais dans popover et que j'utilise un composant popover commun pour cette application, je peux également casser la logique dans d'autres parties de l'application.

Je ne comprends vraiment pas le but de bouillonner dans l'arbre React. Si j'ai besoin d'événements à partir d'un composant de portail, je peux simplement passer des gestionnaires à travers des accessoires. Nous l'avons fait avec unstable_rendersubtreeintocontainer et cela a parfaitement fonctionné.

Si j'ouvre une fenêtre modale à partir d'un bouton situé au fond de l'arbre de réaction, je recevrai un déclenchement inattendu d'événements sous modal. stopPropagation arrêtera également la propagation dans DOM, et je n'obtiendrai pas d'événements que je m'attends vraiment à ce qu'ils soient déclenchés (

@gaearon Je dirais qu'il s'agit plus d'un bogue que d'une demande de fonctionnalité. Nous avons un certain nombre de nouveaux bogues causés par des événements de souris qui se propagent à travers les portails (où nous utilisions auparavant unstable_rendersubtreeintocontainer ). Certains d'entre eux ne peuvent pas être corrigés, même avec une couche div supplémentaire pour filtrer les événements de la souris, car nous comptons par exemple sur les événements mousemove se propageant jusqu'au document pour implémenter des boîtes de dialogue déplaçables.

Existe-t-il un moyen de contourner ce problème avant que cela ne soit résolu dans une future version ?

Je pense que cela s'appelle une demande de fonctionnalité, car le comportement actuel de la bulle des portails est à la fois attendu et voulu. Le but est que les sous-arbres agissent comme de véritables enfants de leurs parents.

Ce qui serait utile, ce sont des cas d'utilisation ou des situations supplémentaires (comme ceux que vous voyez) que vous ne pensez pas être servis par l'implémentation actuelle, ou sont difficiles à contourner

Je comprends que ce comportement est voulu, mais je pense qu'il s'agit d'un bogue important qui ne peut pas être désactivé.

Dans mon esprit, la bibliothèque travaillant avec DOM devrait préserver le comportement d'implémentation DOM et non le casser.

Par exemple:

class Container extends React.Component {
  shouldComponentUpdate = () => false;
  render = () => (
    <div
      ref={this.props.containerRef}
      // Event propagation on this element not working
      onMouseEnter={() => { console.log('handle mouse enter'); }}
      onClick={() => { console.log('handle click'); }}
    />
  )
}

class Root extends React.PureComponent {
  state = { container: null };
  handleContainer = (container) => { this.setState({ container }); }

  render = () => (
    <div>
      <div
        // Event propagation on this element not working also
        onMouseEnter={() => { console.log('handle mouse enter'); }}
        onClick={() => { console.log('handle click'); }}
      >
        <Container containerRef={this.handleContainer} />
      </div>
      {this.state.container && ReactDOM.createPortal(
        <div>Portal</div>,
        this.state.container
      )}
    </div>
  );
}

Lorsque je travaille avec DOM, je m'attends à recevoir des événements comme le fait l'implémentation DOM. Dans mon exemple, les événements sont propagés via Portal , en contournant ses parents DOM, et cela peut être considéré comme un bogue .

Merci à tous pour la discussion, mais je ne pense pas qu'il soit utile de se demander si quelque chose est un bogue ou non. Au lieu de cela, je serais plus productif de discuter des cas d'utilisation et des exemples qui ne sont pas satisfaits par le comportement actuel, afin que nous puissions mieux comprendre si la méthode actuelle est la meilleure pour l'avenir.

En général, nous voulons que l'API gère un ensemble diversifié de cas d'utilisation tout en n'en limitant pas trop les autres, espérons-le. Je ne peux pas parler au nom de l'équipe principale, mais j'imagine que la rendre configurable n'est pas une solution probable. Généralement, React se penche sur une API cohérente plutôt que sur des API configurables.

Je comprends également que ce comportement n'est pas le fonctionnement du DOM, mais je ne pense pas que ce soit en soi une bonne raison de dire qu'il ne devrait pas en être ainsi. Beaucoup de comportement de react-dom est différent de la façon dont fonctionne le DOM, peut-être que les événements sont déjà différents de la version native. onChange par exemple est complètement différent de l'événement de changement natif, et tous les événements réagissent à la bulle quel que soit leur type, contrairement au DOM.

Au lieu de cela, je serais plus productif pour discuter des cas d'utilisation et des exemples qui ne sont pas satisfaits par le comportement actuel

Voici deux exemples qui sont cassés pour nous dans notre migration vers React 16.

Tout d'abord, nous avons une boîte de dialogue déplaçable qui est lancée par un bouton. J'ai tenté d'ajouter un élément de "filtrage" sur notre utilisation du portail qui appelait StopPropagation sur n'importe quel événement de souris * et de clé *. Cependant, nous comptons sur la possibilité de lier un événement mousemove au document afin d'implémenter la fonctionnalité de déplacement -- ceci est courant car si l'utilisateur déplace la souris à une vitesse significative, le curseur quitte les limites de la boîte de dialogue et vous avez besoin pour pouvoir capturer le mouvement de la souris à un niveau supérieur. Le filtrage de ces événements interrompt cette fonctionnalité. Mais avec les portails, les événements de la souris et des touches jaillissent de l'intérieur de la boîte de dialogue vers le bouton qui l'a lancée, ce qui l'amène à afficher différents effets visuels et même à fermer la boîte de dialogue. Je ne pense pas qu'il soit réaliste de s'attendre à ce que chaque composant qui sera lancé via un portail lie 10 à 20 gestionnaires d'événements pour arrêter cette propagation d'événements.

Deuxièmement, nous avons un menu contextuel contextuel qui peut être lancé par un clic de souris principal ou secondaire. L'un des consommateurs internes de notre bibliothèque a des gestionnaires de souris attachés à l'élément qui lance ce menu, et bien sûr le menu a également des gestionnaires de clics pour gérer la sélection des éléments. Le menu réapparaît maintenant à chaque clic alors que les événements mousedown/mousedown remontent jusqu'au bouton qui lance le menu.

Je ne peux pas parler au nom de l'équipe principale, mais j'imagine que la rendre configurable n'est pas une solution probable. Généralement, React se penche sur une API cohérente plutôt que sur des API configurables.

Je vous implore (ainsi que l'équipe) de reconsidérer cette position dans ce cas particulier. Je pense que le bouillonnement d'événements sera intéressant pour certains cas d'utilisation (bien que je ne puisse penser à aucun désinvolte). Mais je pense que cela sera paralysant chez d'autres, et cela introduit une incohérence significative dans l'API. Alors que unstable_rendersubtreeintocontainer n'a jamais été super-supporté, c'était ce que tout le monde avait l'habitude de rendre en dehors de l'arborescence immédiate, et cela ne fonctionnait pas de cette façon. Il a été officiellement déprécié en faveur des portails, mais les portails brisent la fonctionnalité de cette manière critique, et il ne semble pas y avoir de solution de contournement facile. Je pense que cela peut être assez décrit comme assez incohérent.

Je comprends également que ce comportement n'est pas le fonctionnement du DOM, mais je ne pense pas que ce soit en soi une bonne raison de dire qu'il ne devrait pas en être ainsi.

Je comprends d'où vous venez ici, mais je pense que dans ce cas (a) c'est un comportement fondamental qui (b) n'a actuellement aucune solution de contournement, donc je pense que "le DOM ne fonctionne pas de cette façon" est un argument fort, sinon complètement convaincant.

Et pour être clair : ma demande que cela soit considéré comme un bogue est principalement pour qu'il soit priorisé pour un correctif le plus tôt possible.

Mon modèle mental d'un portail est qu'il se comporte comme s'il se trouvait au même endroit dans l'arborescence, mais évite les problèmes tels que "débordement : caché" et évite le défilement à des fins de dessin/mise en page.

Il existe de nombreuses solutions « popup » similaires qui se produisent en ligne sans portail. Par exemple, un bouton qui développe une case juste à côté.

Prenons comme exemple la boîte de dialogue "Choisissez votre réaction" ici sur GitHub. Cela est implémenté comme un div juste à côté du bouton. Cela fonctionne bien maintenant. Cependant, s'il veut avoir un z-index différent, ou être retiré d'une zone overflow: scroll qui contient ces commentaires, il devra alors changer la position du DOM. Ce changement n'est pas sûr à faire à moins que d'autres choses comme le bouillonnement d'événements ne soient également préservées.

Les deux styles de « popups » ou « pop outs » sont légitimes. Alors, comment résoudriez-vous le même problème lorsque le composant est en ligne dans la mise en page au lieu de flotter à l'extérieur de celui-ci ?

La solution de contournement qui a fonctionné pour moi consiste à appeler stopPropagation directement sous le rendu du portail :

return createPortal(
      <div onClick={e => e.stopPropagation()}>{this.props.children}</div>,
      this.el
    )

Cela fonctionne très bien pour moi car j'ai un seul composant d'abstraction qui utilise des portails, sinon vous devrez réparer tous vos appels createPortal .

@methyl cela suppose que vous connaissez tous les événements dont vous avez besoin pour empêcher le bouillonnement de l'arbre. Et dans le cas que j'ai mentionné avec les boîtes de dialogue déplaçables, nous avons besoin de mousemove pour remonter au document, mais pas pour remonter l'arbre de rendu.

Les deux styles de « popups » ou « pop outs » sont légitimes. Alors, comment résoudriez-vous le même problème lorsque le composant est en ligne dans la mise en page au lieu de flotter à l'extérieur de celui-ci ?

@sebmarkbage Je ne suis pas sûr que cette question ait un sens. Si j'avais ce problème d'intégration du composant, je ne l'intégrerais pas.

Je pense que certains des problèmes ici sont que certains cas d'utilisation de renderSubtreeIntoContainer sont portés vers createPortal lorsque les deux méthodes font des choses conceptuellement différentes. Je pense que le concept de Portal était surchargé.

Je suis d'accord que dans le cas de la boîte de dialogue Modal, vous ne voulez presque jamais que le modal agisse comme un enfant du bouton qui l'a ouvert. Le composant déclencheur ne le rend que parce qu'il contrôle l'état open . Je pense que c'est une erreur de dire que l'implémentation du portail est donc fausse, au lieu de dire que createPortal dans le bouton n'est pas le bon outil pour cela. Dans ce cas, le modal n'est pas un enfant du déclencheur et ne doit pas être rendu comme s'il l'était. Une solution possible consiste à continuer à utiliser renderSubtreeIntoContainer , une autre option de l'espace utilisateur consiste à avoir un ModalProvider près de la racine de l'application qui gère le rendu des modaux, et transmet (via le contexte) une méthode pour rendre un élément modal arbitraire besoin de la racine

renderSubtreeIntoContainer ne peut pas être appelé à partir de render ou de méthodes de cycle de vie dans React 16, ce qui exclut pratiquement son utilisation pour les cas dont j'ai parlé (en fait, tous nos composants qui ont été faire cela complètement cassé dans la migration vers 16). Les portails sont la recommandation officielle : https://reactjs.org/blog/2017/09/26/react-v16.0.html#breaking -changes

Je suis d'accord pour dire que le concept de Portails a peut-être fini par être surchargé. Je ne suis pas sûr d'aimer la solution d'un composant global et son contexte, cependant. Il semble que cela puisse être facilement résolu par un indicateur sur createPortal spécifiant si les événements doivent passer. Ce serait un indicateur d'opt-in qui préserverait la compatibilité de l'API avec 16+.

Je vais essayer de clarifier notre cas d'utilisation des portails et pourquoi nous aimerions voir une option pour arrêter la propagation des événements. Dans l'application ManyChat, nous utilisons des portails pour créer des « couches ». Nous avons le système de couches pour l'ensemble de l'application qui est utilisé par plusieurs types de composants : popovers, listes déroulantes, menus, modaux. Chaque couche peut exposer une nouvelle couche, par exemple un bouton sur un deuxième niveau de menu peut déclencher la fenêtre modale où l'autre bouton peut ouvrir le popover. Dans la plupart des cas, la couche est la nouvelle branche de l'UX qui résout sa propre tâche. Et lorsqu'un nouveau calque est ouvert, l'utilisateur doit interagir avec ce nouveau calque, pas avec les autres en bas. Ainsi, pour ce système, nous avons créé un composant commun pour le rendu en calque :

class RenderToLayer extends Component {
  ...
  stop = e => e.stopPropagation()

  render() {
    const { open, layerClassName, useLayerForClickAway, render: renderLayer } = this.props

    if (!open) { return null }

    return createPortal(
      <div
        ref={this.handleLayer}
        style={useLayerForClickAway ? clickAwayStyle : null}
        className={layerClassName}
        onClick={this.stop}
        onContextMenu={this.stop}
        onDoubleClick={this.stop}
        onDrag={this.stop}
        onDragEnd={this.stop}
        onDragEnter={this.stop}
        onDragExit={this.stop}
        onDragLeave={this.stop}
        onDragOver={this.stop}
        onDragStart={this.stop}
        onDrop={this.stop}
        onMouseDown={this.stop}
        onMouseEnter={this.stop}
        onMouseLeave={this.stop}
        onMouseMove={this.stop}
        onMouseOver={this.stop}
        onMouseOut={this.stop}
        onMouseUp={this.stop}

        onKeyDown={this.stop}
        onKeyPress={this.stop}
        onKeyUp={this.stop}

        onFocus={this.stop}
        onBlur={this.stop}

        onChange={this.stop}
        onInput={this.stop}
        onInvalid={this.stop}
        onSubmit={this.stop}
      >
        {renderLayer()}
      </div>, document.body)
  }
  ...
}

Ce composant arrête la propagation de tous les types d'événements à partir des documents React et nous a permis de passer à React 16.

Cela doit-il être lié à des portails ? Plutôt que des portails de sandboxing, et s'il y avait juste un (par exemple) <React.Sandbox>...</React.Sandbox> ?

Même cela me semble inutilement complexe. Pourquoi ne pas simplement ajouter un indicateur booléen facultatif à createPortal permettant de bloquer le comportement de bullage ?

@gaearon c'est une situation assez malheureuse pour une certaine tranche d'entre nous - pourriez-vous ou quelqu'un qui vous est cher y jeter un œil ? :)

J'ajouterais que ma réflexion actuelle est que les deux cas d'utilisation devraient être pris en charge. Il existe vraiment des cas d'utilisation où vous avez besoin que le contexte passe du parent actuel au sous-arbre, mais que ce sous-arbre n'agisse pas comme un enfant logique en termes de DOM. Les modaux complexes sont le meilleur exemple, vous ne voulez presque jamais que les événements d'un formulaire dans une fenêtre modale se propagent jusqu'au bouton de déclenchement, mais vous avez presque certainement besoin du contexte passé (i18n, thèmes, etc.)

Je dirai que ce cas d'utilisation _pourrait_ être principalement résolu avec un ModalProvider plus proche de la racine de l'application qui s'affiche via createPortal suffisamment haut pour que la propagation de l'événement n'affecte rien, mais cela commence à ressembler à une solution de contournement par opposition à un architecture bien conçue. Cela rend également les modaux fournis par la bibliothèque plus ennuyeux pour les utilisateurs car ils ne sont plus autonomes.

J'ajouterais qu'en termes d'API, je ne pense pas que createPortal devrait faire les deux, le cas modal veut vraiment juste utiliser ReactDOM.render (old skool) car il est assez proche d'un arbre distinct _except_ que la propagation du contexte est souvent nécessaire

Nous avons juste dû corriger un bogue extrêmement difficile à diagnostiquer dans le code de gestion de focus de notre application externe en raison de l'utilisation de la solution de contournement publiée par @kib357 .

Plus précisément: appeler stopPropagation sur l'événement de focus synthétique pour l'empêcher de sortir du portail provoque également l'appel de stopPropagation sur l'événement de focus natif dans le gestionnaire capturé de React sur #document, ce qui signifie qu'il n'a pas <body> un autre gestionnaire capturé sur

Le nouveau comportement bouillonnant dans Portals me semble vraiment être un cas minoritaire. Que ce soit l'opinion ou la vérité, pourrions-nous s'il vous plaît obtenir de la traction sur cette question ? Peut-être @gaearon ? Il a quatre mois et fait vraiment mal. Je pense que cela pourrait être décrit à juste titre comme un bogue étant donné qu'il s'agit d'un changement d'API dans React 16 sans solution de contournement totalement sûre.

@craigkovatch Je suis toujours curieux de savoir comment vous

Vous pouvez potentiellement mesurer le popover, insérer un espace réservé vide de la même taille et essayer de l'aligner sur le dessus, mais ce n'est pas ce que les gens font.

Donc, si votre popover doit développer le contenu en place, comme juste à côté du bouton, comment le résoudriez-vous ? Je soupçonne que le modèle qui fonctionne là-bas fonctionnera dans les deux cas et nous devrions simplement recommander le modèle.

Je pense qu'en général, c'est le modèle qui fonctionne dans les deux scénarios :

class Foo extends React.Component {
  state = {
    highlight: false,
    showFlyout: false,
  };

  mouseEnter() {
    this.setState({ highlight: true });
  }

  mouseLeave() {
    this.setState({ highlight: false });
  }

  showFlyout() {
    this.setState({ showFlyout: true });
  }

  hideFlyout() {
    this.setState({ showFlyout: false });
  }

  render() {
    return <>
      <div onMouseEnter={this.mouseEnter} onMouseLeave={this.mouseLeave} className={this.state.highlight ? 'highlight' : null}>
        Hello
        <Button onClick={this.showFlyout} />
      </div>
      {this.state.showFlyout ? <Flyout onHide={this.hideFlyout} /> : null}
    </>;
  }
}

Si le Flyout est un portail, cela fonctionne et aucun événement de souris n'est survolé lorsque vous survolez le portail. Mais plus important encore, cela fonctionne également s'il ne s'agit PAS d'un portail et qu'il doit s'agir d'un flyout en ligne. Aucun stopPropagation nécessaire.

Alors, qu'est-ce qui ne fonctionne pas dans ce modèle pour votre cas d'utilisation ?

@sebmarkbage, nous utilisons les portails d'une manière complètement différente, le rendu dans un conteneur monté en tant qu'enfant final de <body> qui est ensuite positionné, parfois avec un z-index. La documentation de React suggère que cela est plus proche de l'intention de conception ; c'est-à-dire le rendu dans un endroit totalement différent dans le DOM. Il ne me semble pas que nos cas d'utilisation soient suffisamment similaires pour que la discussion appartienne à ce fil. Mais si vous voulez faire un remue-méninges/dépanner ensemble, je serais plus qu'heureux d'en discuter davantage dans un autre forum.

Non, mon cas d'utilisation est les deux . Parfois l'un et parfois l'autre. C'est pourquoi c'est pertinent.

Le <Flyout /> peut choisir d'être rendu dans l'enfant final du corps ou non, mais tant que vous hissez le portail lui-même vers un frère du composant survolé plutôt qu'un enfant de celui-ci, votre scénario fonctionne.

Je pense qu'il existe un scénario plausible où cela n'est pas pratique et vous voulez un moyen de téléporter des choses à partir de composants profondément imbriqués, mais dans ce scénario, vous êtes probablement d'accord avec le contexte étant le contexte du point intermédiaire. Mais je pense à ces deux questions distinctes.

Peut-être avons-nous besoin d'une API de machines à sous pour cela.

class Foo extends React.Component {
  state = {
    showFlyout: false,
  };

  showFlyout() {
    this.setState({ showFlyout: true });
  }

  hideFlyout() {
    this.setState({ showFlyout: false });
  }

  render() {
    return <>
      Hello
      <Button onClick={this.showFlyout} />
      <SlotContent name="flyout">
        {this.state.showFlyout ? <Flyout onHide={this.hideFlyout} /> : null}
      </SlotContent>
    </>;
  }
}

class Bar extends React.Component {
  state = {
    highlight: false,
  };

  mouseEnter() {
    this.setState({ highlight: true });
  }

  mouseLeave() {
    this.setState({ highlight: false });
  }

  render() {
    return <>
      <div onMouseEnter={this.mouseEnter} onMouseLeave={this.mouseLeave} className={this.state.highlight ? 'highlight' : null}>
        <SomeContext>
          <DeepComponent />
        </SomeContext>
      </div>
      <Slot name="flyout" />
    </>;
  }
}

Le portail obtiendrait alors le contexte de Bar, pas DeepComponent. Le contexte et le bouillonnement d'événements partagent alors toujours le même chemin d'arborescence.

@sebmarkbage, le cas modal nécessite généralement un contexte à partir du moment où il est rendu. C'est un cas légèrement unique, je pense, le composant est un enfant logique de la chose qui l'a rendu mais _pas_ structurel (par manque d'un meilleur mot), par exemple, vous voulez généralement des choses comme le contexte de la forme (relay, formik, redux form , peu importe) mais pas les événements DOM à traverser. On finit également par rendre ces modaux assez profonds dans les arbres, à côté de leurs déclencheurs, de sorte qu'ils restent composants et réutilisables, plus que parce qu'ils y appartiennent structurellement

Je pense que ce cas est généralement différent du cas flyout/dropdown que createPortal sert. À confirmer, je pense que le comportement de bullage des portails est bon, mais pas pour les modaux. Je pense également que cela pourrait raisonnablement bien être géré avec Context et une sorte de ModalProvider, mais c'est un peu ennuyeux, en particulier pour les bibliothèques.

tant que vous hissez le portail lui-même vers un frère du composant survolé plutôt qu'un enfant de celui-ci, votre scénario fonctionne.

Je ne suis pas sûr de suivre. Il y a toujours le problème des événements keyDown par exemple qui bouillonnent dans un arbre DOM inattendu.

@jquense Notez que dans mon exemple, l'emplacement est toujours dans le composant Bar, il obtiendrait donc son contexte à partir du formulaire dans quelque chose comme <Form><Bar /></Form> .

Même si le portail est rendu dans le corps du document.

C'est donc comme deux indirections (portails) : profond -> frère de Bar -> corps du document.

Ainsi, le contexte du portail est toujours le contexte du formulaire, de même que la chaîne bouillonnante d'événements, mais ni l'un ni l'autre ne l'est dans le contexte de la chose survolée.

Oui désolé d'avoir raté ça 😳 Si je lis bien, vous auriez quand même le bouillonnement à <Slot> ? C'est certainement mieux, même si je pense que dans le cas du dialogue modal, on ne veut probablement pas de bouillonnement. Comme si vous pensiez en termes de lecteur d'écran, vous voulez que tout en dehors du modal soit inversé, pendant qu'il est actif. Je ne sais pas, je pense que dans ce cas, le bouillonnement est un piège, personne ne s'attendrait à ce qu'un clic à l'intérieur d'une boîte de dialogue bouillonne n'importe où.

Peut-être que le problème ici n'est pas les portails, mais il n'y a pas un bon moyen de partager le contexte entre les arbres ? Une partie du contexte ReactDOM.render est vraiment bien pour les modaux, et peut-être une façon plus "correcte" de penser de toute façon...

Ma pensée ici est qu'il y a un certain bouillonnement car cela va toujours du modal au div au corps au document à la fenêtre. Et conceptuellement, au-delà du cadre jusqu'à la fenêtre contenant et ainsi de suite.

Ce n'est pas théorique dans quelque chose comme le contenu rendu ART ou GL (et dans une certaine mesure React Native) où il pourrait ne pas y avoir d'arbre de support existant pour obtenir cette sémantique. Donc , il doit y avoir un moyen de dire que c'est là où il bouillonne.

Dans certaines applications, il y a des modaux dans les modaux. Par exemple, chez FB, il y a une fenêtre de discussion qui peut être au-dessus d'un modal ou un modal peut faire partie de la fenêtre de discussion. Ainsi, même un modal a un certain contexte quant à l'endroit où il appartient dans l'arbre. Il n'est jamais complètement autonome.

Cela ne veut pas dire que nous ne pouvons pas avoir deux sémantiques différentes pour le bouillonnement d'événements et le contexte. C'est à la fois explicite à ce sujet et vous pouvez portailr l'un sans l'autre, etc.

Avoir la garantie qu'ils suivent tous les deux le même chemin est vraiment puissant car cela signifie que le bouillonnement d'événements peut être entièrement implémenté pour les événements de l'espace utilisateur de la même manière que dans le navigateur.

Par exemple, cela se produit aujourd'hui avec divers contextes Redux. Imaginez que this.context.dispatch("Hover") soit un bouillonnement d'événements dans l'espace utilisateur. Nous pourrions même implémenter des événements React dans le cadre du contexte. Il semble raisonnable de penser que je peux l'utiliser de la même manière, et de toutes les manières en ce moment, vous le pouvez. Je pense que si nous duvions ces deux contextes, nous nous retrouverions probablement avec une autre API de contexte d'espace utilisateur qui suit la structure DOM en parallèle avec le contexte normal - s'ils sont vraiment si différents.

C'est donc un peu la raison pour laquelle je m'y oppose un peu pour voir si le truc des slots pourrait être suffisant, car a) vous devez de toute façon être explicite sur le contexte qui se produit. b) il peut éviter de bifurquer le monde et d'avoir deux systèmes de contexte entiers.

Plus précisément: appeler stopPropagation sur l'événement de focus synthétique pour l'empêcher de sortir du portail provoque l'appel de stopPropagation sur l'événement de focus natif dans le gestionnaire capturé de React sur #document, ce qui signifie qu'il n'a pas atteint un autre gestionnaire capturé sur

. Nous avons corrigé en déplaçant notre gestionnaire jusqu'à #document, mais nous avions spécifiquement évité de le faire dans le passé afin de ne pas marcher sur les pieds de React.

@craigkovatch , avez-vous utilisé l'événement onFocusCapture sur le document ? Dans ma solution de contournement, les événements capturés ne doivent pas être arrêtés. Pouvez-vous fournir un exemple plus détaillé de la façon dont cela s'est passé et de ce que vous avez fait pour résoudre votre problème ?
De plus, je pense que mon code a un problème avec l'arrêt blur événement

@kib357 Je ne suggère pas qu'il y ait un problème dans votre solution de contournement, je pense qu'il y a un bogue distinct dans React (c'est-à-dire qu'il ne devrait pas annuler la propagation des événements de focus natifs en phase de capture lors de l'appel de stopPropagation sur les événements de focus synthétiques en phase de bouillonnement).

Le code en question utilise un écouteur d'événement de capture natif, c'est- document.body.addEventListener('focus', handler, true) dire

@craigkovatch semble intéressant, étant donné que vous avez utilisé le gestionnaire capturé. Cependant, je n'ai aucune idée de pourquoi cela se produit.

Alors, les gars, nous avons deux scénarios différents pour utiliser le rendu du portail :

  1. Pour éviter les problèmes CSS comme overflow:hidden et etc dans des widgets simples, comme des boutons déroulants ou des menus à un niveau
  2. Pour créer une nouvelle couche UX pour des cas plus puissants comme :
  3. modaux
  4. menus imbriqués
  5. popovers-with-forms-with-dropdowns-... - tous les cas, lorsque les calques sont combinés

Je pense que l'API createPortal ne satisfait que le premier scénario. La suggestion d'utiliser un nouveau React.render pour la seconde est inutilisable - il est très difficile de créer une application distincte avec tous ses fournisseurs pour chaque couche.
Quelles informations supplémentaires pouvons-nous fournir pour aider à résoudre ce problème ?
Quels inconvénients du paramètre suggéré dans l'API createPortal ?

@sebmarkbage Ma question immédiate avec l'API slots est la suivante : pourrais-je insérer plusieurs SlotContents dans un Slot en même temps ? Il n'est pas rare dans notre interface d'avoir plusieurs "popups" ou "modals" ouverts simultanément. Dans mon monde parfait, une API Popup ressemblerait à ceci :

import { App } from './app'
import { PopupSlot } from './popups'

let root = (
  <div>
    <App />
    <PopupSlot />
  </div>
)

ReactDOM.render(root, document.querySelector('#root'))

// some dark corner of our app

import { Popup } from './popups'

export function SoManyPopups () {
  return <>
    <Popup>My Entire</Popup>
    <Popup>Interface</Popup>
    <Popup>Is Popups</Popup>
  </>
}

Nous avons un nouveau problème avec cela pour lequel je n'ai pas réussi à trouver de solution de contournement. En utilisant l'approche de « piège à événements » suggérée ci-dessus, seuls les événements React Synthetic sont bloqués et ne peuvent sortir du portail. Les événements natifs bouillonnent toujours, et puisque notre code React est hébergé dans une application principalement jQuery, le gestionnaire global jQuery keyDown sur <body> obtient toujours l'événement.

J'ai essayé d'ajouter un écouteur event.stopPropagation à l'élément conteneur natif à l'intérieur du portail via une référence comme celle-ci, mais cela neutralise complètement tous les événements synthétiques du portail - j'ai supposé à tort que l'écouteur de niveau supérieur de React surveillait la phase de capture.

Je ne sais pas ce qui peut être fait ici, à part des modifications de React.

const allTheEvents: string[] = 'click contextmenu doubleclick drag dragend dragenter dragexit dragleave dragover dragstart drop mousedown mouseenter mouseleave mousemove mouseover mouseout mouseup keydown keypress keyup focus blur change input invalid submit'.split(' ');
const stop = (e: React.SyntheticEvent<HTMLElement>): void => { e.stopPropagation(); };
const nativeStop = (e: Event): void => e.stopPropagation();
const handleRef = (ref: HTMLDivElement | null): void => {
  if (!ref) { return; }
  allTheEvents.forEach(eventName => ref.addEventListener(eventName, nativeStop));
};


/** Prevents https://reactjs.org/docs/portals.html#event-bubbling-through-portals */
export function PortalEventTrap(children: React.ReactNode): JSX.Element {
  return <div
      onClick={stop}
      ...

      ref={handleRef}
    >
      {children}
    </div>;
}

Cela dépend de l'ordre dans lequel ReactDOM et JQuery sont initialisés. Si JQuery s'initialise en premier, les gestionnaires d'événements de niveau supérieur de JQuery seront installés en premier et s'exécuteront donc avant que les gestionnaires synthétiques de ReactDOM ne s'exécutent.

ReactDOM et JQuery préfèrent tous deux n'avoir qu'un seul écouteur de niveau supérieur qui simule ensuite le bouillonnement en interne, à moins qu'il n'y ait un événement empêchant le navigateur de bouillonner, tel que scroll .

@Kovensky, j'ai compris que jQuery ne faisait pas de "bullage synthétique" comme le fait React, et n'a donc pas un seul auditeur de haut niveau. Mon inspecteur DOM n'en révèle pas non plus. J'aimerais bien voir à quoi vous faites référence si je me trompe.

Ce sera le cas pour les événements délégués. Par exemple, $(document.body).on('click', '.my-selector', e => e.stopPropagation()) .

Écoutez, cela peut être résolu dans React, si quelqu'un me convainc que cela ne peut pas être résolu avec la conception proposée ci-dessus, ce qui nécessite une restructuration de votre code. Mais je n'ai vu aucune raison qui ne puisse être faite autre que d'essayer de trouver une solution de contournement rapide.

@sebmarkbage votre proposition ne résout que le cas des événements se propageant au propriétaire immédiat. Et le reste de l'arbre ?

Voici un cas d'utilisation que je pense ne peut pas être bien résolu avec Slots ou createPortal

<Form defaultValue={fromValue}>
   <more-fancy-markup />
   <div>
     <Field name="faz"/>
     <ComplexFieldModal>
       <Field name="foo.bar"/>
       <Field name="foo.baz"/>
     </ComplexFieldModal>
  </div>
</Form>

Et voici un gif avec une configuration similaire mais légèrement différente, où j'utilise createPortal pour un site réactif, pour déplacer un champ de formulaire vers la barre d'outils de l'application (beaucoup plus haut dans l'arborescence). Dans ce cas également, je ne veux vraiment pas que les événements reviennent au contenu de la page, mais je veux vraiment que le contexte du formulaire l'accompagne. Mon implémentation est d'ailleurs quelque chose de Slot-esque utilisant le contexte ...

large gif 640x320

@sebmarkbage unstable_renderSubtreeIntoContainer permettait un accès direct au sommet de la hiérarchie quelle que soit la position d'un composant, soit dans la hiérarchie, soit dans le cadre d'un framework packagé séparé.

Comparativement, je vois quelques problèmes avec la solution Slot :

  • La solution suppose que vous avez accès à la position de la hiérarchie où il est "correct" de faire des bulles d'événements. Ce n'est certainement pas le cas pour les composants et les frameworks de composants.
  • Suppose qu'il est « correct » de diffuser des événements à tout autre niveau de la hiérarchie.
  • Les événements sortiront toujours de la position de Slot. (comme @craigkovatch l'a mentionné)

J'ai aussi un cas d'utilisation (probablement similaire à ceux déjà mentionnés).

J'ai une surface où les utilisateurs peuvent sélectionner des choses avec sa souris avec un "lasso". Il s'agit essentiellement de 100% de largeur/hauteur et est à la racine de mon application et utilise l'événement onMouseDown . Dans cette surface, il y a aussi des boutons qui ouvrent des portails comme des modaux et des listes déroulantes. Un événement mouseDown à l'intérieur du portail est en fait intercepté par le composant de sélection au lasso à la racine de l'application.

J'en vois plusieurs pour régler le problème :

  • rendre le portail un cran au-dessus du composant lasso racine, mais ce n'est pas très pratique et nécessiterait probablement de recourir à une bibliothèque contextuelle telle que react-gateway? (ou peut-être le système de slot mentionné).
  • arrêter la propagation manuellement à l'intérieur de la racine du portail, mais pourrait entraîner les effets secondaires indésirables mentionnés ci-dessus
  • possibilité d'arrêter la propagation dans les portails React (+1 btw)
  • filtrer les événements lorsqu'ils proviennent d'un portail

Pour l'instant, ma solution est de filtrer les événements.

const appRootNode = document.getElementById('root');

const isInPortal = element => isNodeInParent(element, appRootNode);


    handleMouseDown = e => {
      if (!isInPortal(e.target)) {
        return;
      }
      ...
    };

Ce ne sera clairement pas la meilleure solution pour nous tous et ne sera pas très agréable si vous avez des portails imbriqués, mais pour mon cas d'utilisation actuel (qui est le seul actuellement), cela fonctionne. Je ne veux pas ajouter une nouvelle bibliothèque de contexte ou faire un refactor complexe pour résoudre ce problème. Je voulais juste partager ma solution.

J'ai réussi à bloquer le bouillonnement d'événements, comme indiqué ailleurs dans ce fil.

Mais un autre problème apparemment plus épineux que je rencontre est le onMouseEnter SyntheticEvent, qui ne bouillonne pas. Il passe plutôt du parent commun du composant from composant to comme décrit ici . Cela signifie que si le pointeur de la souris entre depuis l'extérieur de la fenêtre du navigateur, chaque gestionnaire onMouseEnter du haut du DOM jusqu'au composant de createPortal sera déclenché dans cet ordre, provoquant le déclenchement de toutes sortes d'événements qui jamais fait avec unstable_renderSubtreeIntoContainer . Étant donné que onMouseEnter ne bouillonne pas, il ne peut pas être bloqué au niveau du portail. (Cela ne semblait pas poser de problème avec unstable_renderSubtreeIntoContainer car l'événement onMouseEnter ne respectait pas la hiérarchie virtuelle et ne s'enchaînait pas à travers le contenu du corps, mais descendait directement dans le sous-arbre.)

Si quelqu'un a des idées sur la façon d'empêcher les événements onMouseEnter de se propager à partir du haut de la hiérarchie DOM ou de se détourner directement vers la sous-arborescence du portail, faites-le moi savoir.

@JasonGore J'ai également remarqué ce comportement.

Par exemple.

J'ai un menu contextuel qui est rendu lorsqu'un div se déclenche surMouseOver, puis j'ouvre un modal avec createPortal en cliquant sur l'un des éléments du menu. Lorsque je sors la souris de la fenêtre du navigateur, l'événement onMouseLeave se propage jusqu'au menu contextuel, fermant le menu contextuel (et donc le modal)...

J'ai eu le même problème où j'avais un élément de liste que je voulais que l'intégralité soit cliquable (sous forme de lien), mais je voulais un bouton de suppression sur les étiquettes sous le nom qui ouvrirait un modal pour confirmer.

screenshot 2018-10-31 at 11 42 47

Ma seule solution était d'éviter les bulles sur la division modale comme ceci:

// components/Modal.js

onClick(e) {
    e.stopPropagation();
}

return createPortal(
        <div onClick={this.onClick} ...
            ...

Cela évitera de bouillonner sur chaque modal oui, mais je n'ai aucun cas où je voudrais que cela se produise pour le moment, donc cela fonctionne pour moi.

Y a-t-il des problèmes potentiels avec cette approche?

@jnsandrew n'oubliez pas qu'il y a environ 50 autres types d'événements qui bouillonnent 🙃

Frappez juste ceci. Il me semble gênant que React se comporte à sa manière, ce qui est différent du bouillonnement d'événements DOM.

+1 à cela. Nous utilisons React.createPortal pour effectuer le rendu à l'intérieur de l'iframe (pour les styles et l'isolement des événements) et ne pas être en mesure d'empêcher les événements de sortir de la boîte est une déception.

On dirait que c'est le 12e problème le plus signalé dans le backlog de React. Au moins, les documents sont ouverts à ce sujet https://reactjs.org/docs/portals.html#event -bubbling-through-portals - bien qu'ils ne mentionnent pas les inconvénients ou les solutions de contournement et note à la place qu'il permet "des abstractions plus flexibles " :

Les documents devraient au moins expliquer que cela peut causer des problèmes et suggérer des solutions de contournement. Dans mon cas, c'est un cas d'utilisation assez simple, en utilisant https://github.com/reactjs/react-modal : j'ai des boutons qui ouvrent des choses comme des listes déroulantes, et à l'intérieur se trouvent des boutons qui créent des modaux. En cliquant sur le bouton des bulles modales jusqu'en haut, il fait des choses indésirables. Les modaux sont encapsulés dans un composant cohérent et le retrait de la partie portaile casse cette encapsulation, créant une abstraction qui fuit. Une solution de contournement peut consister à retourner un indicateur pour désactiver ces boutons lorsque le modal est ouvert. Et bien sûr, je peux également arrêter la propagation comme suggéré ci-dessus, bien que dans certains cas, je ne veuille pas le faire.

Je ne sais pas à quel point le bouillonnement et la capture sont utiles en général (bien que je sache que React repose sur le bouillonnement sous le capot) - ils ont certainement une histoire riche, mais je préfère passer un rappel ou propager un événement plus spécifique (par exemple, redux) que de faire des bulles ou de capturer, car de telles choses passent probablement par un tas d'intermédiaires inutiles. Il y a des articles comme https://css-tricks.com/dangers-stopping-event-propagation/ et je travaille sur des applications qui dépendent de la propagation au corps, principalement pour fermer les choses en cliquant sur "extérieur", mais je préfère mettez une superposition invisible sur tout et fermez en cliquant dessus. Bien sûr, je ne pouvais pas utiliser le portail de React pour créer une superposition aussi invisible...

Il y a aussi un cauchemar de maintenance ici - au fur et à mesure que de nouveaux événements sont ajoutés au DOM, tous les portails "scellés" avec la technique discutée ci-dessus "divulgueront" ces nouveaux événements jusqu'à ce que les responsables puissent les ajouter à la (extensive) liste noire.

Il y a ici un problème de conception majeur qui doit être résolu. La possibilité d'activer ou de désactiver le bouillonnement inter-portail me semble toujours la meilleure option d'API. Je ne suis pas sûr de la difficulté de mise en œuvre, mais nous obtenons toujours des bogues de production à ce sujet dans Tableau, plus d'un an plus tard.

Passez 2 heures à essayer de savoir pourquoi mon formulaire de modal soumettait un autre formulaire.
J'ai enfin compris grâce à ce problème !

J'ai vraiment du mal à voir quand la propagation de onSubmit peut être nécessaire. Très probablement, cela ressemblera toujours plus à un bogue qu'à une fonctionnalité.

Au moins, cela vaut la peine d'ajouter quelques informations d'avertissement pour réagir aux docs . Quelque chose comme ça:
Bien que la diffusion d'événements à travers les portails soit une fonctionnalité intéressante, vous souhaiterez parfois empêcher la propagation d'événements. Vous pouvez y parvenir en ajoutant onSubmit={(e) => {e.stopPropagation()}}

+1 à cela aussi. Nous utilisons intensivement des draftjs avec du texte cliquable montrant les modaux. Et tous les événements sur le modal comme la mise au point, la sélection, le changement, l'appui sur une touche, etc. font juste exploser les brouillons avec des erreurs.

OMI, le comportement de proxy d'événement est fondamentalement cassé (et me cause également des bugs), mais je reconnais que cela est controversé. Ce fil suggère assez fortement qu'il existe un besoin pour un portail qui contient le contexte des trous de ver mais pas les événements . L'équipe de base est-elle d'accord ? Quoi qu'il en soit, quelle est la prochaine étape ici ?

Je ne peux pas vraiment comprendre pourquoi la propagation d'événements à partir du portail est un comportement intentionnel ? Cela va complètement à l'encontre de l'idée principale de propagation. Je pensais que les portails avaient été créés précisément pour éviter ce genre de choses (comme l'imbrication manuelle, la propagation d'événements, etc.).

Je peux confirmer que si vous placez le portail près de l'arbre des éléments, il propagera les événements :

class SomeComponent extends React.Component<any, any> {
  render() {
    return <>
      <div className="some-tree">
        // Portal here will bubble events
      </div>
      // Portal here will also bubble events, just checked
    </>
  }
}

+1 pour cette demande de fonctionnalité

Dans DOM, les événements remontent dans l'arborescence DOM. Dans React, les événements remontent dans l'arborescence des composants.

Je m'appuie un peu sur le comportement existant, dont un exemple est les popouts qui peuvent être imbriqués; ce sont tous des portails pour éviter les problèmes avec overflow: hidden , mais pour que la fenêtre contextuelle se comporte correctement, je dois détecter les clics externes sur le composant contextuel (ce qui est différent de la détection de clics en dehors des éléments DOM rendus) . Il peut y avoir de meilleurs exemples.

Je pense que la discussion vigoureuse ici a clairement montré qu'il y a de bonnes raisons d'avoir les deux comportements. Étant donné que createPortal restitue un composant React à l'intérieur d'un nœud de conteneur "plain DOM", je ne pense pas qu'il serait possible que les événements synthétiques de React se propagent à partir d'un portail jusqu'à l'arborescence plain-old-DOM.

Étant donné que les portails sont disponibles depuis longtemps maintenant, il est probablement trop tard pour modifier le comportement par défaut afin de « ne pas se propager au-delà des limites du portail ».

Sur la base de toutes les discussions jusqu'à présent, ma proposition la plus simple est alors (encore) : ajouter un indicateur facultatif à createPortal qui empêche toute propagation d'événement au-delà de la limite du portail.

Quelque chose de plus robuste pourrait être la possibilité de fournir une liste blanche d'événements qui devraient être autorisés à "passer à travers" la limite, tout en arrêtant le reste.

@gaearon Sommes-nous au point que l'équipe React pourrait réellement s'en charger ? Il s'agit d'un des 10 premiers problèmes, mais nous n'avons rien entendu de votre part à ce sujet depuis un bon moment.

Je souhaite ajouter mon soutien à cela et ne suis pas d'accord avec les commentaires de

La possibilité de porter le contexte d'un endroit du DOM à un autre est utile pour implémenter toutes sortes de superpositions telles que des info-bulles, des listes déroulantes, des cartes de survol et des boîtes de dialogue, où le contenu de la superposition est décrit et rendu dans le contexte de, le déclencheur. Le contexte étant un concept React, ce mécanisme résout un problème React. D'un autre côté, la possibilité de portailr des événements DOM bouillonnant d'un endroit dans le DOM à un autre est une astuce sophistiquée qui vous permet de prétendre que la structure du DOM est différente de ce que vous avez explicitement configuré pour qu'elle soit. Cela résout un problème lié à l'utilisation de la diffusion d'événements DOM pour la délégation, lorsque vous souhaitez déléguer à une autre partie du DOM. Vous devriez probablement utiliser des rappels (ou un contexte) de toute façon, si vous avez React, plutôt que de vous fier aux événements DOM qui bouillonnent de l'intérieur vers l'extérieur de la superposition. Comme d'autres l'ont souligné, vous souhaitez rarement « atteindre » et gérer un événement qui se produit à l'intérieur de la superposition, intentionnellement ou non.

Le bouillonnement d'événements DOM résout principalement un problème de correspondance des événements DOM avec les cibles DOM. Chaque clic est en fait un clic sur tout un ensemble d'éléments imbriqués. Il n'est pas préférable de le considérer comme un mécanisme de délégation de haut niveau, IMO, et l'utilisation d'événements DOM pour déléguer à travers les limites des composants React n'est pas une excellente encapsulation, à moins que les composants ne soient de petits composants d'assistance privés utilisés pour restituer des bits prévisibles de DOM.

event.target === event.currentTarget m'aide à résoudre ce problème. Mais c'est vraiment un casse-tête.

Cela m'a mordu aujourd'hui en essayant de migrer un composant popover en utilisant unstable_renderSubtreeIntoContainer pour utiliser createPortal . Le composant en question contient des éléments déplaçables et est rendu comme un descendant d'un autre élément déplaçable. Cela signifie que les éléments parent et popover contiennent des gestionnaires d'événements de souris et tactiles, qui ont tous deux commencé à se déclencher lors de l'interaction avec le popover du portail.

Étant donné que unstable_renderSubtreeIntoContainer est obsolète (?), une solution alternative est nécessaire - aucune des solutions de contournement présentées ci-dessus ne semble être une solution viable à long terme.

Hey! Merci pour toutes ces suggestions les gars!
Cela m'a aidé à résoudre un de mes problèmes.
Souhaitez-vous lire un article intéressant et informatif sur l'importance et les capacités de l' équipe React ? Je suppose que ce sera utile pour tous ceux qui s'intéressent au développement. Bonne chance!

OMI, le plus souvent, vous voulez qu'un portail vous donne accès au contexte, mais pas aux événements. À l'époque où nous utilisions Angular 1.x, nous écrivions notre propre service contextuel qui prendrait $scope et une chaîne de modèle, et compilerait/rendrait ce modèle et l'ajouterait au corps. Nous avons implémenté tous les popups/modals/dropdowns de notre application avec ce service, et pas une seule fois nous n'avons manqué l'absence de bouillonnement d'événements.

La solution stopPropagation() contournement semble empêcher les écouteurs d'événements natifs sur window de se déclencher (dans notre cas ajouté par react-dnd-html5-backend ).

Voici une reproduction minimale du problème : https://codepen.io/mogel/pen/xxKRPbQ

S'il n'est pas prévu de fournir un moyen d'éviter le bouillonnement synthétique à travers les portails, peut-être que quelqu'un a une solution de contournement qui n'interrompt pas le bouillonnement d'événements natifs ?

La solution de contournement stopPropagation() semble empêcher les écouteurs d'événements natifs sur la fenêtre de se déclencher

Correct. :(

S'il n'est pas prévu de fournir un moyen d'éviter le bouillonnement synthétique à travers les portails

Malgré le silence de l'équipe de base, moi, et beaucoup d'autres sur ce fil, espérons vraiment qu'il existe de tels plans.

peut-être que quelqu'un a une solution de contournement qui n'interrompt pas le bouillonnement d'événements natifs ?

La solution de contournement de mon équipe a été d'interdire les portails entièrement à cause de ce problème flagrant. Nous présentons des volets avec un crochet dans un conteneur qui vit dans les autres contextes de l'application, de sorte que vous obtenez gratuitement des contextes de niveau racine ; tous les autres que nous traversons manuellement. Pas génial, mais mieux que des gestionnaires d'événements inutiles.

Cela fait 17 mois depuis la dernière réponse par quelqu'un de l'équipe de base. Peut-être qu'un ping pourrait attirer l'attention sur ce problème :) @sebmarkbage ou @gaearon

La solution de contournement de mon équipe a été d'interdire les portails entièrement à cause de ce problème flagrant. Nous présentons des volets avec un crochet dans un conteneur qui vit dans les autres contextes de l'application, de sorte que vous obtenez gratuitement des contextes de niveau racine ; tous les autres que nous traversons manuellement. Pas génial, mais mieux que des gestionnaires d'événements inutiles.

Je ne peux pas penser à des approches génériques pour transmettre le contexte dans le "faux portail" via des accessoires sans revenir à compter sur des accessoires en cascade :(

D'innombrables bugs que j'ai détectés sur https://github.com/reakit/reakit étaient liés à ce problème. J'utilise beaucoup React Portal et je ne peux pas penser à un seul cas où je voulais que des événements bouillonnent des portails vers leurs composants parents.

Ma solution de contournement a été soit de la vérifier dans mes gestionnaires d'événements parents :

event.currentTarget.contains(event.target);

Ou en utilisant des événements natifs à la place :

const onClick = () => {};
React.useEffect(() => {
  ref.current.addEventListener("click", onClick);
  return () => ref.current.removeEventListener("click", onClick);
});

J'utilise ces approches en interne dans la bibliothèque. Mais aucun d'eux n'est idéal. Et, puisqu'il s'agit d'une bibliothèque de composants open source, je ne peux pas contrôler la façon dont les gens transmettent les gestionnaires d'événements aux composants.

Une option pour désactiver le bouillonnement d'événements résoudrait tous ces problèmes.

J'ai piraté une semi-solution de contournement qui bloque le bouillonnement de React tout en redéclenchant un clone de l'événement sur window . Il semble fonctionner dans Chrome, Firefox et Safari sur OSX, mais IE11 est laissé de côté car il n'autorise pas la définition manuelle de event.target . Jusqu'à présent, il ne s'occupe que des événements de souris, de pointeur, de clavier et de molette. Je ne sais pas si les événements de glisser seraient possibles à cloner.

Malheureusement, il n'est pas utilisable dans notre base de code car nous avons besoin du support IE11, mais peut-être que quelqu'un d'autre pourrait l'adapter pour son propre usage.

Ce qui rend cela particulièrement ahurissant, c'est que le comportement "par défaut" fait à nouveau remonter l'arborescence des composants. Prenons l'arbre suivant :

<Link>
   <Menu (portal)>
      <form onSubmit={...}>
         <button type="submit">

Je me suis arraché les cheveux pendant des heures, pourquoi avec cette combinaison exacte de composants, le onSubmit de mon formulaire n'est jamais appelé, que je clique sur le bouton d'envoi ou que j'appuie sur Entrée dans un champ de saisie à l'intérieur du formulaire.

Enfin, j'ai découvert que c'est parce que le composant React Router Link a une implémentation onClick qui fait e.preventDefault() pour empêcher le navigateur de se recharger. Cependant, cela a pour effet secondaire malheureux de bloquer également le comportement par défaut du clic sur le bouton de soumission, qui se trouve être la soumission du formulaire. Donc, ce que j'ai appris aujourd'hui, c'est que le onSubmit est en fait appelé par le navigateur, comme action par défaut pour appuyer sur le bouton de soumission. Même lorsque vous appuyez sur Entrée, cela déclenche un clic sur le bouton d'envoi, déclenchant ainsi l'envoi d'un formulaire.

Mais vous voyez comment l'ordre de propagation des événements rend cela vraiment très étrange.

  1. <input> [appuyer sur la touche entrée]
  2. <button type="submit"> [clic simulé]
  3. <Menu> [l'événement se propage à l'extérieur du portail]
  4. <Link> [la propagation atteint le parent Link ]
  5. <Link> [appels e.preventDefault() ]
  6. => la réponse par défaut du navigateur au clic sur le bouton de soumission est annulée
  7. => le formulaire n'est pas soumis

Cela se produit même si nous avons déjà transmis le bouton et le formulaire dans le DOM, et que Link n'a rien à voir avec cela et n'avait pas non plus l'intention de bloquer ce comportement du tout.

La solution pour moi (si quelqu'un rencontre le même problème) était la solution couramment utilisée consistant à envelopper le contenu <Menu> dans un div avec onClick={e => e.stopPropagation()} . Mais ce que je veux dire, c'est que j'ai perdu beaucoup de temps à rechercher le problème parce que le comportement n'est vraiment pas intuitif.

La solution pour moi (si quelqu'un rencontre le même problème) était la solution couramment utilisée consistant à envelopper le contenu <Menu> dans un div avec onClick={e => e.stopPropagation()} . Mais ce que je veux dire, c'est que j'ai perdu beaucoup de temps à rechercher le problème parce que le comportement n'est vraiment pas intuitif.

Oui, chaque _instance individuelle du problème_ a la même solution simple, _une fois que vous avez rencontré le bogue et l'avez correctement identifié_. C'est un gouffre d'échec aux parois très abruptes que l'équipe React a creusé ici, et c'est frustrant de ne pas en entendre parler de leur part.

J'ai passé plusieurs jours à essayer de déboguer un autre problème avec des mouseenter jaillissaient des portails de manière inattendue. Même avec onMouseEnter={e => e.stopPropagation()} sur le portail div, les événements bouillonnent toujours vers le bouton propriétaire, comme dans https://github.com/facebook/react/issues/11387#issuecomment -340009465 (le premier commenter cette question). mouseenter / mouseleave ne sont pas censés bouillonner en premier lieu...

Peut-être encore plus étrange, quand je vois un événement synthétique mouseenter jaillir d'un portail vers un bouton, e.nativeEvent.type est mouseout . React déclenche un événement synthétique non bouillonnant basé sur un événement natif bouillonnant - et malgré l'appel de stopPropagation sur l'événement synthétique.

@gaearon @trueadm ce problème a causé une énorme frustration constante depuis plus de deux ans maintenant. Ce fil est l'un des principaux problèmes actifs sur React. S'il vous plaît , quelqu'un de l'équipe pourrait-il contribuer ici?

Dans mon cas, l'ouverture du composant Window en cliquant sur un bouton a fait disparaître la fenêtre car le clic sur Window a provoqué un clic sur le bouton qui a provoqué le changement d'état

Je suis nouveau avec React, j'utilise principalement jQuery et vanillia JS mais c'est un bug époustouflant. Il peut y avoir environ 1% des cas où ce comportement serait attendu...

J'aime les deux solutions de @diegohaz , mais je pense toujours que createPortal devrait avoir une option pour arrêter le bouillonnement d'événements.

Mon cas d'utilisation particulier était avec les gestionnaires onMouseLeave et onMouseEnter d'une info-bulle déclenchés par le descendant du portail de son enfant - ce qui n'était pas souhaité. Les événements natifs ont résolu ce problème en ignorant les descendants du portail car ils ne sont pas des descendants de dom.

+1 pour l'option d'arrêter de bouillonner dans les portails. Il a été suggéré de simplement placer le portail en tant que frère (au lieu d'un enfant) du composant d'où provient l'écouteur d'événement, mais je pense que cela ne fonctionne pas dans de nombreux cas d'utilisation (y compris le mien).

Il semble enfin que ReactDOM.unstable_renderSubtreeIntoContainer sera supprimé , ce qui signifie qu'il n'y aura bientôt plus de solutions de contournement raisonnables pour ce problème...

^ aidez-nous @trueadm -nobi vous êtes notre seul espoir

Il semble que les pinger sur GitHub ne fonctionne pas 😞
Peut-être que quelqu'un avec un compte Twitter actif pourrait tweeter à ce sujet, en taguant l'un des contributeurs ?

Ajout de mon +1 pour ce problème. Chez Notion, nous utilisons actuellement une implémentation de portail personnalisée antérieure à React.createPortal , et nous transmettons manuellement nos fournisseurs de contexte à la nouvelle arborescence. J'ai essayé d'adopter React.createPortal mais j'ai été bloqué par le comportement de bouillonnement inattendu :

La suggestion de @sebmarkbage de déplacer le <Portal> dehors du composant <MenuItem> pour devenir un frère ne résout le problème que pour un seul niveau d'imbrication. Le problème persiste si vous avez plusieurs éléments de menu imbriqués (par exemple) qui font sortir des sous-menus.

Ce problème a été automatiquement marqué comme obsolète. Si ce problème vous affecte toujours, veuillez laisser un commentaire (par exemple, "bump"), et nous le garderons ouvert. Nous sommes désolés de n'avoir pas encore pu le prioriser. Si vous avez de nouvelles informations supplémentaires, veuillez les inclure dans votre commentaire!

Cogner.

Dan a laissé un commentaire sur un problème connexe :

@mogelbrod Je n'ai rien à ajouter pour le moment, mais quelque chose comme ça ( #11387 (commentaire) ) me semble raisonnable si vous migrez un composant existant.

Suivi par Dan dans le même numéro :

Merci pour le contexte sur la solution de contournement. Puisque vous avez déjà cette connaissance du domaine, la meilleure prochaine étape est probablement d'écrire une RFC pour le comportement que vous souhaitez et les alternatives que vous avez envisagées : https://github.com/reactjs/rfcs. Gardez à l'esprit qu'un RFC disant "changeons simplement cela" est peu susceptible d'être utile. Écrire un bon RFC nécessite à la fois de comprendre pourquoi nous avons le comportement actuel, _et_ un plan pour le changer d'une manière qui convient à vos cas d'utilisation sans régresser sur les autres.

Indépendamment de cela , unstable_renderSubtreeIntoContainer n'est pas pris en charge, alors débrouillons ces deux discussions. Nous n'y ajouterons pas la propagation de Context car l'ensemble de l'API est gelé et ne sera pas mis à jour.

Nous devrions certainement publier une RFC React pour suggérer l'ajout du drapeau discuté, ou peut-être une autre solution. Quelqu'un se sent-il particulièrement intéressé à en rédiger un (peut-être @justjake , @craigkovatch ou @jquense) ? Sinon je vais voir ce que je peux trouver !

Bien que je sois intéressé à faire évoluer cette API, je n'ai pas d'intérêt à rédiger un RFC. Surtout parce que c'est un tas de travail et il n'y a presque aucune chance qu'il soit accepté, je ne pense pas que l'équipe de base considère réellement les RFC qui ne sont pas déjà sur leur feuille de route.

@jquense Je ne pense pas que ce soit exact. Oui, il est peu probable que nous fusionnions une RFC qui ne correspond pas à la vision, car l'ajout d'une nouvelle API est toujours transversal et influence toutes les autres fonctionnalités prévues. Et c'est juste que nous ne commentons pas souvent ceux qui ne fonctionnent pas. Cependant, nous les lisons, et surtout lorsque nous abordons un sujet sur lequel l'écosystème a plus d'expertise. Comme exemples, https://github.com/reactjs/rfcs/pull/38 , https://github.com/ reactjs/rfcs/pull/150 , https://github.com/reactjs/rfcs/pull/118 , https://github.com/reactjs/rfcs/pull/109 , https://github.com/reactjs/ rfcs/pull/32 ont tous influencé notre réflexion même si nous ne les avons pas explicitement commentés.

En d'autres termes, nous abordons les RFC en partie comme un mécanisme de recherche communautaire. Ce commentaire de @mogelbrod (https://github.com/facebook/react/issues/16721#issuecomment-674748100) expliquant pourquoi la solution de contournement est ennuyeuse est exactement le genre de chose que nous aimerions voir dans un RFC. Un examen des solutions existantes et de leurs inconvénients peut être plus précieux qu'une proposition de suggestion d'API concrète.

@gaearon Mon commentaire ne veut pas dire que l'équipe n'écoute pas les commentaires extérieurs. Tu en feras du bon travail. Je pense que mon commentaire est exact. Le _process_ tel qu'il se déroule sur le référentiel RFC n'entraîne pas l'acceptation de RFC par d'autres personnes. En regardant quels RFC sont fusionnés, ce sont entièrement les membres de l'équipe principale ou les employés de fb et personne d'autre. Les fonctionnalités qui le font sont généralement un peu différentes et ne participent pas du tout au processus RFC (par exemple, les identifiants isomorphes).

Je suis très heureux d'entendre que vous regarderez les autres RFC et qu'ils contribuent à la conception des fonctionnalités, mais "Nous avons été influencés par ces RFC en dehors même si nous ne les avons jamais commentés", je pense illustre mon point, pas le défier.

En d'autres termes, nous abordons les RFC en partie comme un mécanisme de recherche communautaire.

C'est super raisonnable, mais ce n'est pas ce que le référentiel RFC dit que _son_ approche est, et pas ce que les autres pensent généralement des RFC. Le processus RFC est généralement un lien et un point de communication entre l'équipe et la communauté, ainsi qu'une sorte de terrain de jeu égal en termes de prise en compte et de processus des fonctionnalités.

Points plus importants sur la gouvernance communautaire de côté. Demander aux gens de passer du temps à rédiger des propositions détaillées, puis de les défendre auprès d'autres participants extérieurs tout en étant confronté au silence de l'équipe de réaction est décourageant et renforce activement l'impression que FB ne se soucie que de ses propres besoins en matière d'OSS. Ce qui pue parce que je sais que tu ne te sentiras pas ou ne concevras pas de cette façon.

Si le processus RFC est censé être : « voici où vous pouvez décrire vos préoccupations et cas d'utilisation et nous les lirons, quand/si nous arrivons à un point de pouvoir implémenter cette fonctionnalité ». Honnêtement, c'est une bonne approche. Je pense que la communauté bénéficierait de ce qui est explicitement énoncé, sinon ppl assumera (et assumera) le même niveau d'implication et de participation que les autres processus RFC et sera ensuite activement découragé lorsque cela ne se produira pas. J'ai certainement cette impression même avec un peu plus de perspicacité que les autres contributeurs.

Bien sûr, je pense que je suis d'accord avec tout cela. Je ne veux pas en faire un méta-fil, mais juste dire que puisque les gens continuent de pinger sur ce fil, la chose la plus concrète à faire pour choses est d'écrire une proposition sur la façon dont cela devrait fonctionner qui prend en compte les préoccupations des deux côtés en compte et compile une compréhension profonde de l'espace de problème . Je comprends tout à fait si ce n'est pas quelque chose dans lequel les gens aimeraient mordre les dents (en partie en fonction de la façon dont nous répondons aux RFC), c'est pourquoi je ne l'ai pas suggéré plus tôt - mais comme je continue de recevoir des pings personnels, je voulais suggérer que comme option pour quelqu'un qui est motivé.

assez juste, ce n'est pas le bon endroit pour obtenir des méta sur les RFC :)

@gaearon c'est le 6ème numéro le plus voté actuellement ouvert sur React, et le 4ème le plus commenté. Il est ouvert depuis la sortie de React 16 et n'est qu'à 2 mois d'avoir 3 ans maintenant. Pourtant, il y a eu très peu d'engagement de la part de l'équipe principale de React. C'est très dédaigneux de dire "c'est à la communauté de proposer une solution" après que beaucoup de temps et de douleur se soient écoulés et se soient produits. Veuillez reconnaître que, bien qu'il ait des applications très utiles, ce comportement par défaut était une erreur de conception. Il ne devrait pas appartenir à la communauté RFC de le réparer.

Je regrette d'avoir commenté ce problème et je retire ma suggestion concernant la communauté RFC. Tu as raison, c'est probablement une mauvaise idée. Je dois ajouter que ce problème est devenu très chargé d'émotion, et en tant qu'être humain, je trouve personnellement qu'il est difficile de m'y engager – même si je comprends que c'est important et que beaucoup de gens le pensent fortement.

Permettez-moi de répondre brièvement sur l'état de ce fil.

Tout d'abord, je tiens à m'excuser auprès des personnes qui ont commenté et qui ont été frustrées par le fait que nous n'avons pas continué les suivis dans ce fil. Si j'avais lu ce numéro de l'extérieur, j'aurais probablement eu l'impression que l'équipe React a fait une erreur, ne veut pas l'admettre et est prête à s'asseoir sur une solution simple ("ajoutez juste un booléen, combien que ce soit!") pendant plus de deux ans parce qu'ils ne se soucient pas de la communauté. Je peux tout à fait comprendre comment vous pourriez arriver à cette conclusion.

Je sais que cette question est hautement votée. Cela a été évoqué plusieurs fois dans ce fil, peut-être du point de vue que si l'équipe React avait su qu'il s'agissait d'un gros problème, nous l'aurions résolu plus tôt. Nous savons que c'est un point douloureux - les gens nous envoient régulièrement des messages privés à ce sujet, ou le présentent comme un exemple de la façon dont l'équipe React ne se soucie pas de la communauté. Bien que je reconnaisse pleinement que le silence a été frustrant, la pression croissante pour « juste faire quelque chose » a rendu plus difficile de traiter ce problème de manière productive.

Ce problème a des solutions de contournement, ce qui le rend différent d'une vulnérabilité de sécurité ou d'un plantage qui doit être traité de toute urgence. Nous savons que les wokarounds fonctionnent (mais ne sont pas idéaux et peuvent être ennuyeux) parce que nous en utilisons certains nous-mêmes, en particulier autour du code écrit avant React 16. Je pense que nous pouvons convenir que même si ce problème a sans aucun doute été frustrant pour un grand nombre de personnes, il s'agit toujours d'une classe de problèmes différente de celle d'un crash ou d'un problème de sécurité auquel il faut répondre dans un délai concret.

De plus, je ne suis pas d'accord avec l'idée qu'il existe une solution simple que nous pouvons mettre en œuvre demain. Même si nous considérons le comportement initial comme une erreur (avec laquelle je ne suis pas sûr d'être d'accord), cela signifie que la barre pour que le comportement suivant gère toute la variété des cas d'utilisation est encore plus élevée . Si nous réparons certains cas mais en cassons d'autres, nous n'avons fait aucun progrès et avons créé une tonne de désabonnement. Gardez à l'esprit que nous n'entendrons pas parler des cas où le comportement actuel fonctionne bien dans ce problème. Nous n'en entendrons parler qu'après l'avoir rompu.

Pour vous donner un exemple, le comportement actuel est en fait très utile pour le cas d'utilisation de la gestion de la focalisation déclarative que nous recherchons depuis un certain temps. Il est utile de traiter le focus/flou comme se produisant "à l'intérieur" d'un modal en ce qui concerne l'arborescence des pièces, bien qu'il s'agisse d'un portail. Si nous devions envoyer la proposition "simple" createPortal(tree, boolean) suggérée dans ce fil de discussion, ce cas d'utilisation ne fonctionnerait pas car le portail lui -

Les événements en particulier sont un domaine épineux, par exemple nous venons d'apporter un tas de changements qui règlent des années de problèmes, et cela a été une grande priorité cette année. Mais nous ne pouvons faire qu'un certain nombre de choses à la fois.

Généralement, en tant qu'équipe, nous essayons de nous concentrer sur quelques problèmes en profondeur, plutôt que sur de nombreux problèmes de manière superficielle. Malheureusement, cela signifie que certains défauts et lacunes conceptuels peuvent ne pas être comblés pendant des années parce que nous sommes en train de corriger d'autres lacunes importantes ou que nous n'avons pas élaboré de conception alternative qui aurait résolu le problème pour de bon. Je sais que c'est frustrant à entendre, et c'est en partie pourquoi je suis resté loin de ce fil. Certains autres fils de discussion similaires se sont transformés en explications plus approfondies des problèmes et des solutions possibles, ce qui est utile, mais celui-ci s'est principalement transformé en un flot de "+1" et de suggestions pour un correctif "simple", c'est pourquoi cela a été difficile de s'y engager de manière significative.

Je sais que ce n'est pas la réponse que les gens voulaient entendre, mais j'espère que c'est mieux que pas de réponse du tout.

Une autre chose qui vaut la peine d'être signalée est que certains des problèmes décrits dans ce fil pourraient avoir été résolus par d'autres moyens. Par exemple:

Plus précisément: appeler stopPropagation sur l'événement de focus synthétique pour l'empêcher de sortir du portail provoque l'appel de stopPropagation sur l'événement de focus natif dans le gestionnaire capturé de React sur #document, ce qui signifie qu'il n'a pas atteint un autre gestionnaire capturé sur

React n'utilise plus la phase de capture pour émuler le bouillonnement et n'écoute plus les événements sur le document. Donc, sans écarter la frustration, il faudra certainement réévaluer tout ce qui a été publié jusqu'à présent à la lumière des autres changements.

Les événements natifs bouillonnent toujours, et comme notre code React est hébergé dans une application principalement jQuery, le gestionnaire global jQuery keyDown sur

obtient toujours l'événement.

De même, React 17 attachera des événements aux racines et aux conteneurs de portail (et arrêtera en fait la propagation native à ce stade), donc je m'attendrais à ce qu'il soit également résolu.

Concernant les points sur la suppression de renderSubtreeIntoContainer . Littéralement, sa seule différence avec ReactDOM.render est qu'il propage le contexte hérité. Étant donné que toute version qui n'inclurait pas renderSubtreeIntoContainer n'inclurait pas non plus Legacy Context, ReactDOM.render resterait une alternative 100 % identique. Bien sûr, cela ne résout pas le problème plus large, mais je pense que la préoccupation concernant renderSubtree particulier est quelque peu déplacée.

@gaearon

Concernant les points sur la suppression de renderSubtreeIntoContainer . Littéralement, sa seule différence avec ReactDOM.render est qu'il propage le contexte hérité. Étant donné que toute version qui n'inclurait pas renderSubtreeIntoContainer n'inclurait pas non plus Legacy Context, ReactDOM.render resterait une alternative 100 % identique. Bien sûr, cela ne résout pas le problème plus large, mais je pense que la préoccupation concernant renderSubtree particulier est quelque peu déplacée.

Maintenant que vous l'avez mentionné, je me demande si le code ci-dessous serait une implémentation valide et sûre pour un portail React sans bouillonnement d'événements :

function Portal({ children }) {
  const containerRef = React.useRef();

  React.useEffect(() => {
    const container = document.createElement("div");
    containerRef.current = container;
    document.body.appendChild(container);
    return () => {
      ReactDOM.unmountComponentAtNode(container);
      document.body.removeChild(container);
    };
  }, []);

  React.useEffect(() => {
    ReactDOM.render(children, containerRef.current);
  }, [children]);

  return null;
}

CodeSandbox avec quelques tests : https://codesandbox.io/s/react-portal-with-reactdom-render-m22dj?file=/src/App.js

Il y a toujours un problème avec la non-transmission du contexte moderne mais ce n'est pas un nouveau problème ( renderSubtree est également affecté par cela). La solution de contournement consiste à entourer votre arborescence d'un ensemble de fournisseurs de contexte. Dans l'ensemble, il n'est pas idéal d'imbriquer des arbres, je ne recommanderais donc pas de passer à ce modèle dans autre chose que les scénarios de code existants hérités.

Encore une fois, merci beaucoup pour l'article @gaearon !

Il semble que l'agrégation de la liste des cas cassés + solutions de contournement (mise à jour pour React v17) serait la chose la plus productive pour quelqu'un en dehors de l'équipe principale (corrigez-moi si je me trompe !).

Je suis submergé par les semaines à venir mais j'ai pour objectif de le faire dès que possible. Si quelqu'un d'autre est capable de le faire plus tôt, ou de sonner avec des extraits (comme @diegohaz vient de le faire), ce serait génial !

Agréger une liste de cas serait certainement utile, même si je dirais qu'il doit inclure non seulement les cas cassés, mais aussi les cas où le comportement actuel est logique.

S'il y a un espace public à ajouter, je serais heureux d'ajouter des cas d'utilisation de nos applications et en tant qu'auteur de bibliothèque d'interface utilisateur. En général, je suis d'accord avec Dan sur le fait que, bien que parfois ennuyeux, il est facile de contourner le problème. Pour les cas où vous souhaitez que React bouillonne, il est très difficile de couvrir le cas sans l'aide de React.

Agréger une liste de cas serait certainement utile, même si je dirais qu'il doit inclure non seulement les cas cassés, mais aussi les cas où le comportement actuel est logique.

Je serais heureux de les inclure si quelqu'un peut m'indiquer un code source ouvert/un code extrait qui en dépend ! Comme vous l'avez mentionné précédemment, c'est un peu difficile à trouver car seules les personnes ayant des problèmes avec le comportement actuel sont impliquées dans ce problème 😅

S'il y a un espace public à ajouter, je serais heureux d'ajouter des cas d'utilisation de nos applications et en tant qu'auteur de bibliothèque d'interface utilisateur. En général, je suis d'accord avec Dan sur le fait que, bien que parfois ennuyeux, il est facile de contourner le problème. Pour les cas où vous souhaitez que React bouillonne, il est très difficile de couvrir le cas sans l'aide de React.

Un espace spécifique que vous avez en tête, ou le partage d'un codeandbox (ou jsfiddle, etc.) par cas fonctionnerait-il comme un démarreur ? Je peux essayer de les compiler tous une fois que nous aurons rassemblé quelques cas.

J'ai commencé un fil ici : https://github.com/facebook/react/issues/19637. Restons concentrés sur des exemples pratiques, tandis que celui-ci reste pour une discussion générale.

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