React: Le clonage de l'enfant d'un contexte Consumer produit un avertissement et une erreur déroutants

Créé le 25 avr. 2018  ·  4Commentaires  ·  Source: facebook/react

Voulez-vous demander une fonctionnalité ou signaler un bogue ?
il s'agit d'un bogue, ou du moins d'une demande d'avertissements et de messages d'erreur plus précis.

Quel est le comportement actuel?

J'étais en train de cloner des enfants pour ajouter des propriétés et j'ai oublié que le sous-arbre consommateur de contexte ne devrait pas être cloné ...

import React from 'react';
import {render} from 'react-dom';

const { Provider, Consumer} = React.createContext();

const Comp = ({children})=> <Provider>{cloneKids(children)}</Provider>;

const cloneKids=(children)=>React.Children.map(children, child =>
                           React.cloneElement(child, child.props,
                                  child.props.children&&
                                  cloneKids(child.props.children)));
render(
    <Comp><Consumer>{console.log}</Consumer></Comp>,
    document.getElementById('root')
);

Le code produit l'avertissement et l'erreur introduits avec # 12241

Avertissement: un consommateur de contexte a été rendu avec plusieurs enfants ou un enfant qui n'est pas une fonction. Un consommateur de contexte attend un seul enfant qui est une fonction. Si vous avez passé une fonction, assurez-vous qu'il n'y a pas d'espace blanc de fin ou de début autour d'elle.

et (encore plus déroutant)

TypeError: le rendu n'est pas une fonction

Quel est le comportement attendu?

Peut-être que React.cloneElement ne devrait pas tenter de cloner des fonctions? Quoi qu'il fasse, le résultat n'est pas une fonction.

La partie d'avertissement "un enfant qui n'est pas une fonction" doit être séparée des autres avertissements. Il ne peut pas y avoir plusieurs enfants et un enfant qui ne soit pas une fonction en même temps, donc un avertissement plus précis peut être émis.

Quelles versions de React et quel navigateur / système d'exploitation sont concernés par ce problème?

Testé avec react 16.3.0 dans Stackblitz / Chrome 65 et react 16.3.2 dans Chrome 65 et Firefox 59

Question

Commentaire le plus utile

Le problème n'est pas avec cloneElement (il n'a pas de logique spéciale pour le contexte), mais que vous passez props.children (qui est une fonction dans ce cas) à Children.map() (qui n'attend pas de fonctions). Children.map ne fonctionne qu'avec les nœuds React normaux.

Peut-être que Children.map pourrait afficher un avertissement lorsqu'il rencontre quelque chose qui n'est pas un nœud React.

Dans les deux cas, le clonage profond des arbres React comme celui-ci donne l'impression que vous essayez de faire quelque chose pour lequel React n'a pas été conçu. Pourquoi en avez-vous besoin?

Tous les 4 commentaires

Le problème n'est pas avec cloneElement (il n'a pas de logique spéciale pour le contexte), mais que vous passez props.children (qui est une fonction dans ce cas) à Children.map() (qui n'attend pas de fonctions). Children.map ne fonctionne qu'avec les nœuds React normaux.

Peut-être que Children.map pourrait afficher un avertissement lorsqu'il rencontre quelque chose qui n'est pas un nœud React.

Dans les deux cas, le clonage profond des arbres React comme celui-ci donne l'impression que vous essayez de faire quelque chose pour lequel React n'a pas été conçu. Pourquoi en avez-vous besoin?

Merci!

Je retarde l'évaluation de quelques propriétés enfants jusqu'à ce que le parent soit prêt à fournir un contexte pour leur évaluation. Par exemple:

<Parent ><Child prefix-prop="string expression"></Parent>
(le langage d'expression n'est pas javascript et il est évalué à distance. Je ne contrôle pas Child, et il peut y avoir n'importe quel nombre d'enfants).

L'enfant devient intérieurement quelque chose comme:

<Child prop={evaluate("string expression")} />

Pour ce faire, le parent visite les enfants et transforme la propriété "prefix-prop" contenant l'expression en propriété "prop", qui contient la valeur de l'expression. Le clonage est la seule approche que j'ai trouvée pour modifier les propriétés des composants. Bien sûr, le contexte Les consommateurs ne doivent pas être clonés, il n'y a pas de propriétés à y modifier.

Je sais que je devrais utiliser des fonctions de fonction en tant qu'enfant ou de rendu ou similaires (pour retarder l'évaluation) mais je conçois une bibliothèque de prototypage et mes utilisateurs (qui viennent de HTML et JSP, et sont souvent novices au-delà du HTML et du langage d'expression) n'appréciera probablement pas cette notation:

<Parent>{ context=> <Child prop={context.evaluate(x)} />}</Parent>

(d'ailleurs, s'ils oublient un espace juste avant la fonction, ils seront confondus par # 12689)

et ils ne comprendront pas non plus rapidement ce formulaire:
<Parent display={ context=> <Child prop={context.evaluate(x)} />} />

Les consommateurs de contexte utilisent également la fonction en tant qu'enfant afin qu'ils aient le même problème.

C'est un travail très précoce, donc j'envisage toujours des options, mais je veux en quelque sorte arriver à une notation parent-enfant propre comme dans la première citation. Je devrais probablement écrire un plugin babel qui transforme la notation "propre" en une notation fonctionnelle.

Mais peut-être que je manque quelque chose alors j'apprécierais toute contribution. J'aimerais vraiment que mes utilisateurs quittent JSP et adoptent React mais si la notation est trop cryptique, cela n'arrivera pas ...

Une autre idée avec laquelle je joue est un parent fonctionnel, qui retourne probablement Component (une sorte de HOC)

({context})=>
context.parent("context expression",
     context2=><Child prop={context2.evaluate("sub-expression")} context={context2} />)

où Child peut contenir à nouveau des appels context.parent (), et ainsi de suite.

Bien sûr, parent () pourrait utiliser un consommateur de contexte dans le render () du composant qu'il retourne, de sorte que le prop de contexte peut être supprimé:

()=>
parent("context expression",
     context2=><Child prop={context2.evaluate("sub-expression")} />)

_Le problème ici est que parent () étant un composant peut accéder au contexte React, mais evaluer () ne peut pas (à ma connaissance) accéder au contexte React à moins qu'il ne retourne un composant_

ce qui n'est pas possible lorsque vous essayez réellement de définir une propriété.

Par conséquent, je pense que l' accès au contexte React à partir de composants extérieurs serait idéal. Quelque chose comme

React.getContext(Consumer, data=> ...)

Si je me souviens bien, lorsque j'ai eu du mal avec cela, j'ai essayé d'appeler Consumer.render () manuellement en utilisant le framework de test, mais je n'ai rien obtenu de significatif.

PS: La première notation ci-dessus peut sembler cryptique mais elle est en fait assez familière avec la forme JSP actuelle donc je pense que je peux la vendre aux utilisateurs :)

<context:parent ctx="context-expression">
   HTML...<context:evaluate expr="sub-expression" />...HTML
</context:parent>

Je vais fermer car cela ne semble pas être souvent le cas.

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