React: コンテキストコンシューマーの子のクローンを作成すると、紛らわしい警告とエラーが発生します

作成日 2018年04月25日  ·  4コメント  ·  ソース: facebook/react

機能をリクエストしバグを報告しますか?
これはバグであるか、少なくともより正確な警告とエラーメッセージの要求です。

現在の動作は何ですか?

いくつかのプロパティを追加するために子のクローンを作成していましたが、コンテキストコンシューマサブツリーをクローンしないでください...

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

このコードは、#12241で導入された警告とエラーを生成します

警告:コンテキストコンシューマーは、複数の子、または関数ではない子でレンダリングされました。 コンテキストコンシューマーは、関数である単一の子を期待します。 関数を渡した場合は、その周囲に末尾または先頭の空白がないことを確認してください。

および(さらに紛らわしい)

TypeError:レンダリングは関数ではありません

期待される動作は何ですか?

たぶんReact.cloneElementは関数のクローンを作成しようとすべきではありませんか? それが何をするにしても、結果は関数ではありません。

「機能ではない子」という警告部分は、他の警告とは別にする必要があります。 複数の子と、同時に機能しない子が1つ存在することはできないため、より正確な警告を発行できます。

Reactのどのバージョン、およびどのブラウザ/ OSがこの問題の影響を受けますか?

Stackblitz / Chrome65ではreact16.3.0でテストされ、Chrome65およびFirefox59ではreact16.3.2でテストされました。

Question

最も参考になるコメント

問題はcloneElement (コンテキスト用の特別なロジックはありません)ではなく、 props.children (この場合は関数)をChildren.map()渡していることです。 (これは関数を期待していません)。 Children.mapは、通常のReactノードでのみ機能します。

おそらくChildren.mapは、Reactノードではない何かに遭遇したときに警告を表示する可能性があります。

どちらの場合も、このようなReactツリーのクローンを深く作成することは、Reactが設計されていないことを実行しようとしているように聞こえます。 なぜこれが必要なのですか?

全てのコメント4件

問題はcloneElement (コンテキスト用の特別なロジックはありません)ではなく、 props.children (この場合は関数)をChildren.map()渡していることです。 (これは関数を期待していません)。 Children.mapは、通常のReactノードでのみ機能します。

おそらくChildren.mapは、Reactノードではない何かに遭遇したときに警告を表示する可能性があります。

どちらの場合も、このようなReactツリーのクローンを深く作成することは、Reactが設計されていないことを実行しようとしているように聞こえます。 なぜこれが必要なのですか?

ありがとう!

親が評価のコンテキストを提供する準備ができるまで、いくつかの子プロパティの評価を延期しています。 例えば:

<Parent ><Child prefix-prop="string expression"></Parent>
(式言語はjavascriptではなく、リモートで評価されます。私は子を制御していません。子はいくつあってもかまいません)。

子は内部的に次のようになります。

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

これを実現するために、親は子を訪問し、式を含む「prefix-prop」プロパティを、式の値を含む「prop」プロパティに変換します。 コンポーネントのプロパティを変更するために私が見つけた唯一のアプローチは、クローン作成です。 もちろん、コンテキストコンシューマーは複製されるべきではなく、そこで変更するプロパティはありません。

子としての関数やレンダリング関数などを使用する必要があることはわかっていますが(評価を遅らせるため)、プロトタイピングライブラリとユーザー(HTMLとJSPから来ており、HTMLと表現言語以外の初心者であることが多い)を設計しています。おそらくこの表記を理解しないでしょう:

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

(さらに、関数の直前のスペースを忘れると、#12689で混乱します)

そして、彼らはこのフォームをすぐには理解しません:
<Parent display={ context=> <Child prop={context.evaluate(x)} />} />

コンテキストコンシューマーも子としての関数を使用するため、同じ問題が発生します。

これは非常に初期の作業なので、まだオプションを検討していますが、最初の引用のように、どういうわけかきれいな親子表記に到達したいと思います。 おそらく、「クリーンな」表記を機能的な表記に変換するbabelプラグインを作成する必要があります。

しかし、何かが足りないので、何か入力していただければ幸いです。 ユーザーにJSPを離れて、Reactを採用してもらいたいのですが、表記がわかりにくい場合、これは起こりません...

私がいじっているもう1つのアイデアは、機能的な親であり、おそらくコンポーネント(ある種のHOC)を返します。

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

ここで、Childにはcontext.parent()呼び出しなどを再度含めることができます。

もちろん、parent()は、返すコンポーネントのrender()でコンテキストConsumerを使用できるため、コンテキストプロパティを削除できます。

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

_ここでの問題は、コンポーネントであるparent()はReactコンテキストにアクセスできますが、evaluate()は(私の知る限り)Componentを返さない限りReactコンテキストにアクセスできないことです_

これは、実際にプロパティを設定しようとしているときには不可能です。

したがって、外部コンポーネントからReactコンテキストにアクセスするのが理想的だと思います。 何かのようなもの

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

記憶が正しければ、これに苦労しているときに、テストフレームワークを使用してConsumer.render()を手動で呼び出そうとしましたが、意味のあるものは何も得られませんでした。

PS:上記の最初の表記はわかりにくいように見えるかもしれませんが、実際には現在のJSPフォームに精通しているので、ユーザーに販売できると思います:)

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

これは頻繁に発生するようには思われないので、閉じる予定です。

このページは役に立ちましたか?
0 / 5 - 0 評価