React: Clonando o filho de um contexto O consumidor produz avisos e erros confusos

Criado em 25 abr. 2018  ·  4Comentários  ·  Fonte: facebook/react

Você quer solicitar um recurso ou relatar um bug ?
isso é um bug, ou pelo menos uma solicitação de avisos mais precisos e mensagens de erro.

Qual é o comportamento atual?

Eu estava clonando crianças para adicionar algumas propriedades e esqueci que a subárvore de contexto do consumidor não deveria ser clonada ...

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

O código produz o aviso e o erro introduzidos com # 12241

Aviso: um consumidor de contexto foi renderizado com vários filhos ou um filho que não é uma função. Um consumidor de contexto espera um único filho que seja uma função. Se você passou uma função, certifique-se de que não haja nenhum espaço em branco à direita ou à esquerda ao redor dela.

e (ainda mais confuso)

TypeError: render não é uma função

Qual é o comportamento esperado?

Talvez React.cloneElement não deva tentar clonar funções? Faça o que fizer, o resultado não é uma função.

A parte de aviso "um filho que não é uma função" deve ser separada dos outros avisos. Não pode haver vários filhos e um filho que não seja uma função ao mesmo tempo, portanto, um aviso mais preciso pode ser emitido.

Quais versões do React e quais navegadores / sistemas operacionais são afetados por esse problema?

Testado com o react 16.3.0 no Stackblitz / Chrome 65 e o react 16.3.2 no Chrome 65 e Firefox 59

Question

Comentários muito úteis

O problema não é com cloneElement (não tem nenhuma lógica especial para contexto), mas que você está passando props.children (que é uma função neste caso) para Children.map() (que não espera funções). Children.map só funciona com nós React regulares.

Talvez Children.map possa mostrar um aviso ao encontrar algo que não seja um nó React.

Em qualquer dos casos, clonar profundamente árvores React como esta soa como se você estivesse tentando fazer algo para o qual o React não foi projetado. Por que você precisa disso?

Todos 4 comentários

O problema não é com cloneElement (não tem nenhuma lógica especial para contexto), mas que você está passando props.children (que é uma função neste caso) para Children.map() (que não espera funções). Children.map só funciona com nós React regulares.

Talvez Children.map possa mostrar um aviso ao encontrar algo que não seja um nó React.

Em qualquer dos casos, clonar profundamente árvores React como esta soa como se você estivesse tentando fazer algo para o qual o React não foi projetado. Por que você precisa disso?

Obrigado!

Estou atrasando a avaliação de algumas propriedades filho até que o pai esteja pronto para fornecer contexto para sua avaliação. Por exemplo:

<Parent ><Child prefix-prop="string expression"></Parent>
(a expressão linguagem não é javascript e é avaliada remotamente. Eu não controlo Criança, e pode haver qualquer número de crianças).

A criança se torna internamente algo como:

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

Para isso, o pai visita os filhos e transforma a propriedade "prefix-prop" que contém a expressão, na propriedade "prop", que contém o valor da expressão. A clonagem é a única abordagem que descobri para alterar as propriedades do componente. Claro que os consumidores de contexto não devem ser clonados, não há propriedades para alterar lá.

Eu sei que devo usar funções de função como criança ou de renderização ou semelhantes (para atrasar a avaliação), mas estou projetando uma biblioteca de prototipagem e meus usuários (que vêm de HTML e JSP e muitas vezes são novatos além de HTML e a linguagem de expressão) provavelmente não apreciará esta notação:

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

(além disso, se eles esquecerem um espaço antes da função, eles serão confundidos por # 12689)

e eles também não compreenderão rapidamente esta forma:
<Parent display={ context=> <Child prop={context.evaluate(x)} />} />

Os consumidores de contexto também usam função como filho, portanto, têm o mesmo problema.

Este é um trabalho muito inicial, então ainda estou considerando as opções, mas de alguma forma quero chegar a uma notação pai-filho limpa, como na primeira citação. Provavelmente eu deveria escrever um plugin babel que transforma a notação "limpa" em funcional.

Mas talvez eu esteja faltando alguma coisa, então agradeço qualquer contribuição. Eu realmente gostaria que meus usuários deixassem o JSP e adotassem o React, mas se a notação for muito enigmática, isso não vai acontecer ...

Outra ideia com a qual estou brincando é um pai funcional, provavelmente retornando Component (algum tipo de HOC)

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

onde Child pode conter novamente chamadas context.parent () e assim por diante.

Claro, parent () poderia usar um Consumidor de contexto no render () do componente que ele retorna, então a prop de contexto pode ser descartada:

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

_O problema aqui é que parent () sendo um Component pode acessar o contexto React, mas evaluate () não pode (que eu saiba) acessar o contexto React a menos que retorne um Component_

o que não é possível quando você está tentando definir uma propriedade.

Portanto, acredito que acessar o contexto React de componentes externos seria o ideal. Algo como

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

Se bem me lembro, ao lutar com isso, tentei invocar Consumer.render () manualmente usando a estrutura de teste, mas não obtive nada significativo.

PS: A primeira notação acima pode parecer misteriosa, mas na verdade é bastante familiarizada com a forma JSP atual, então acho que posso vendê-la aos usuários :)

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

Vou fechar, pois não parece que isso acontece com frequência.

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