React: La clonación del hijo de un consumidor de contexto produce advertencias y errores confusos

Creado en 25 abr. 2018  ·  4Comentarios  ·  Fuente: facebook/react

¿Quieres solicitar una función o informar de un error ?
esto es un error, o al menos una solicitud de advertencias y mensajes de error más precisos.

¿Cuál es el comportamiento actual?

Estaba clonando niños para agregar algunas propiedades y pasé por alto que el subárbol del consumidor de contexto no debería clonarse ...

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

El código produce la advertencia y el error introducidos con # 12241

Advertencia: un consumidor de contexto se representó con varios elementos secundarios o un elemento secundario que no es una función. Un consumidor de contexto espera un solo hijo que sea una función. Si pasó una función, asegúrese de que no haya espacios en blanco iniciales o finales a su alrededor.

y (aún más confuso)

TypeError: render no es una función

¿Cuál es el comportamiento esperado?

¿Quizás React.cloneElement no debería intentar clonar funciones? Haga lo que haga, el resultado no es una función.

La parte de advertencia "un niño que no es una función" debe separarse de las otras advertencias. No puede haber varios niños y un niño que no sea una función al mismo tiempo, por lo que se puede emitir una advertencia más precisa.

¿Qué versiones de React y qué navegador / sistema operativo se ven afectados por este problema?

Probado con react 16.3.0 en Stackblitz / Chrome 65 y react 16.3.2 en Chrome 65 y Firefox 59

Question

Comentario más útil

El problema no es con cloneElement (no tiene una lógica especial para el contexto), sino que estás pasando props.children (que es una función en este caso) a Children.map() (que no espera funciones). Children.map solo funciona con nodos React regulares.

Quizás Children.map podría mostrar una advertencia cuando encuentre algo que no sea un nodo React.

En cualquier caso, la clonación profunda de árboles de React como este suena como si estuvieras tratando de hacer algo para lo que React no fue diseñado. ¿Por qué necesitas esto?

Todos 4 comentarios

El problema no es con cloneElement (no tiene una lógica especial para el contexto), sino que estás pasando props.children (que es una función en este caso) a Children.map() (que no espera funciones). Children.map solo funciona con nodos React regulares.

Quizás Children.map podría mostrar una advertencia cuando encuentre algo que no sea un nodo React.

En cualquier caso, la clonación profunda de árboles de React como este suena como si estuvieras tratando de hacer algo para lo que React no fue diseñado. ¿Por qué necesitas esto?

¡Gracias!

Estoy retrasando la evaluación de algunas propiedades secundarias hasta que el padre esté listo para proporcionar un contexto para su evaluación. Por ejemplo:

<Parent ><Child prefix-prop="string expression"></Parent>
(El lenguaje de expresión no es javascript y se evalúa de forma remota. No controlo Child, y puede haber cualquier número de niños).

El niño se convierte internamente en algo como:

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

Para lograr eso, el padre visita a los hijos y transforma la propiedad "prefix-prop" que contiene la expresión, en la propiedad "prop", que contiene el valor de la expresión. La clonación es el único enfoque que encontré para alterar las propiedades de los componentes. Por supuesto, los consumidores de contexto no deben ser clonados, no hay propiedades que alterar allí.

Sé que debería usar función como niño o funciones de renderizado o similares (para retrasar la evaluación) pero estoy diseñando una biblioteca de prototipos y mis usuarios (que provienen de HTML y JSP, y a menudo son novatos más allá de HTML y el lenguaje de expresión) lo más probable es que no aprecie esta notación:

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

(además, si olvidan un espacio justo antes de la función, serán confundidos por # 12689)

y tampoco comprenderán rápidamente este formulario:
<Parent display={ context=> <Child prop={context.evaluate(x)} />} />

Los consumidores de contexto también usan la función como niño, por lo que tienen el mismo problema.

Este es un trabajo muy temprano, así que todavía estoy considerando opciones, pero de alguna manera quiero llegar a una notación limpia entre padres e hijos como en la primera cita. Probablemente debería escribir un complemento de babel que transforme la notación "limpia" en una funcional.

Pero tal vez me esté perdiendo algo, así que agradecería cualquier comentario. Realmente me gustaría que mis usuarios dejaran JSP y adoptaran React, pero si la notación es demasiado críptica, esto no sucederá ...

Otra idea con la que estoy jugando es un padre funcional, probablemente devolviendo Componente (algún tipo de HOC)

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

donde Child puede contener nuevamente llamadas context.parent (), y así sucesivamente.

Por supuesto, parent () podría usar un consumidor de contexto en el render () del componente que devuelve, por lo que la propiedad de contexto se puede eliminar:

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

_El problema aquí es que parent () siendo un Componente puede acceder al contexto de React, pero evaluar () no puede (que yo sepa) acceder al contexto de React a menos que devuelva un Component_

lo cual no es posible cuando en realidad está intentando establecer una propiedad.

Por lo tanto, creo que acceder al contexto de React desde componentes externos sería ideal. Algo como

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

Si mal no recuerdo, cuando luchaba con esto, traté de invocar Consumer.render () manualmente usando el marco de prueba, pero no obtuve nada significativo.

PD: La primera notación anterior puede parecer críptica, pero en realidad está bastante familiarizada con el formulario JSP actual, así que creo que puedo venderlo a los usuarios :)

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

Ir a cerrar ya que esto no parece que surja a menudo.

¿Fue útil esta página
0 / 5 - 0 calificaciones