私は一般的な方法で使用できるいくつかのコンポーネントを定義するための適切な方法を見つけようとしています。
<Parent>
<Child value="1">
<Child value="2">
</Parent>
もちろん、親コンポーネントと子コンポーネントの間でレンダリングを行うロジックがあります。このロジックの例として<select>
と<option>
を想像できます。
これは質問の目的のためのダミーの実装です。
var Parent = React.createClass({
doSomething: function(value) {
},
render: function() {
return (<div>{this.props.children}</div>);
}
});
var Child = React.createClass({
onClick: function() {
this.props.doSomething(this.props.value); // doSomething is undefined
},
render: function() {
return (<div onClick={this.onClick}></div>);
}
});
問題は、ラッパーコンポーネントを定義するために{this.props.children}
を使用するときはいつでも、どのようにしてすべての子にプロパティを渡すのでしょうか。
あなたは子を反復処理するために React.Children を使い、それから React.cloneElement を使って各要素を新しい小道具(浅いマージ)で複製することができます。
const Child = ({ doSomething, value }) => (
<div onClick={() => doSomething(value)}>Click Me</div>
);
class Parent extends React.PureComponent {
doSomething = (value) => {
console.log('doSomething called by child with value:', value);
}
render() {
const { children } = this.props;
const childrenWithProps = React.Children.map(children, child =>
React.cloneElement(child, { doSomething: this.doSomething })
);
return <div>{childrenWithProps}</div>
}
};
ReactDOM.render(
<Parent>
<Child value="1" />
<Child value="2" />
</Parent>,
document.getElementById('container')
);
もう少しきれいな方法を試してみてください。
<div>
{React.cloneElement(this.props.children, { loggedIn: this.state.loggedIn })}
</div>
注 :これは子供が一人の場合にのみ有効で、それは有効なReact要素です。
これを試して
<div>{React.cloneElement(this.props.children, {...this.props})}</div>
それはreact-15.1を使って私のために働いた。
他のすべての回答を見る
コンテキストは、現在の認証済みユーザー、テーマ、優先言語など、Reactコンポーネントのツリーに対して「グローバル」と見なすことができるデータを共有するように設計されています。 1
免責事項:これは更新された回答です。以前のものは古いコンテキストAPIを使用していました
それは消費者/提供原則に基づいています。まず、あなたのコンテキストを作りましょう
const { Provider, Consumer } = React.createContext(defaultValue);
それから
<Provider value={/* some value */}>
{children} /* potential consumers */
<Provider />
そして
<Consumer>
{value => /* render something based on the context value */}
</Consumer>
プロバイダの子孫であるすべてのコンシューマは、プロバイダのバリュープロップが変更されるたびに再レンダリングされます。 プロバイダからその下位コンシューマへの伝播はshouldComponentUpdateメソッドの影響を受けないため、祖先コンポーネントが更新を回避してもコンシューマは更新されます。 1
完全な例、半疑似コード。
import React from 'react';
const { Provider, Consumer } = React.createContext({ color: 'white' });
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: { color: 'black' },
};
}
render() {
return (
<Provider value={this.state.value}>
<Toolbar />
</Provider>
);
}
}
class Toolbar extends React.Component {
render() {
return (
<div>
<p> Consumer can be arbitrary levels deep </p>
<Consumer>
{value => <p> The toolbar will be in color {value.color} </p>}
</Consumer>
</div>
);
}
}
React 16.6 へのアップデートで、 React.createContext と contextType を使うことができます。
import * as React from 'react';
// React.createContext accepts a defaultValue as the first param
const MyContext = React.createContext();
class Parent extends React.Component {
doSomething = (value) => {
// Do something here with value
};
render() {
return (
<MyContext.Provider value={{ doSomething: this.doSomething }}>
{this.props.children}
</MyContext.Provider>
);
}
}
class Child extends React.Component {
static contextType = MyContext;
onClick = () => {
this.context.doSomething(this.props.value);
};
render() {
return (
<div onClick={this.onClick}>{this.props.value}</div>
);
}
}
// Example of using Parent and Child
import * as React from 'react';
class SomeComponent extends React.Component {
render() {
<Parent>
<Child value={1} />
<Child value={2} />
</Parent>
}
}
React.createContext どこに輝く React.cloneElement caseはネストしたコンポーネントを処理できなかった
class SomeComponent extends React.Component {
render() {
<Parent>
<Child value={1} />
<SomeOtherComp><Child value={2} /></SomeOtherComp>
</Parent>
}
}
あなたはReact.cloneElement
を使うことができます、あなたがそれをあなたのアプリケーションで使い始める前にそれがどのように働くかを知っているほうが良いです。これはReact v0.13
で紹介されています。詳細についてはこちらを参照してください。
<div>{React.cloneElement(this.props.children, {...this.props})}</div>
それで、Reactのドキュメントからあなたがそれらのすべてがどのように機能するのか、そしてあなたがそれらをどのように利用することができるのか理解するための行を持って来なさい
React v0.13 RC2では、 React.addons.cloneWithPropsに似た新しいAPIを次のシグネチャで紹介します。
React.cloneElement(element, props, ...children);
CloneWithPropsとは異なり、この新しい関数にはstyleとclassNameをマージするための魔法の組み込み動作がありません。同じ理由で[.____。] transferPropsToの機能はありません。まさに完全な魔法のもののリストであることを確信する者は誰もいないので、コードについて推論することは難しく、またスタイルが別のシグネチャを持つ場合は再利用が難しくなります(例:今後のReact Native).
React.cloneElementは、次のものとほぼ同じです。
<element.type {...element.props} {...props}>{children}</element.type>
ただし、JSXやcloneWithPropsとは異なり、参照も保持されます。これは、あなたがその上にrefを持った子供を手に入れたとしても、あなたが誤ってあなたの先祖からそれを盗むことはないということを意味します。新しい要素にも同じ参照番号が付けられます。
一般的なパターンの1つは、子供をマッピングして新しいプロップを追加することです。 cloneWithPropsがrefを失い、コードを推論するのが難しくなるという多くの問題が報告されました。 cloneElementと同じパターンをたどると、期待通りに動作します。例えば:
var newChildren = React.Children.map(this.props.children, function(child) {
return React.cloneElement(child, { foo: true })
});
注意:React.cloneElement(child、{ref: 'newRef'})は refをオーバーライドするので、2人の親が同じ子へのrefを持つことはまだ不可能です。 callback-refsを使用してください。
これは小道具が不変になったのでReact 0.13に入るための重要な機能でした。アップグレードの道はしばしば要素を複製することですが、そうすることによってあなたは参照を失うかもしれません。したがって、ここではより良いアップグレードパスが必要でした。 Facebookでコールサイトをアップグレードしていたとき、私たちはこのメソッドが必要であることに気付きました。私たちはコミュニティから同じフィードバックを得ました。したがって、への最後のリリースの前に、もう一度RCを作成してこれを確認してください。
最終的にReact.addons.cloneWithPropsを非推奨にする予定です。私たちはまだそれをやっていません、しかし、これはあなた自身の用途について考え始め、代わりにReact.cloneElementを使うことを検討する良い機会です。私たちが実際にそれを削除する前に、非推奨の通知とともにリリースを出荷するようにしますので、すぐに対処する必要はありません。
もっと ここ ...
もう{this.props.children}
は必要ありません。これで、render
にRoute
を使って子コンポーネントをラップし、いつものように小道具を渡すことができます。
<BrowserRouter>
<div>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/posts">Posts</Link></li>
<li><Link to="/about">About</Link></li>
</ul>
<hr/>
<Route path="/" exact component={Home} />
<Route path="/posts" render={() => (
<Posts
value1={1}
value2={2}
data={this.state.data}
/>
)} />
<Route path="/about" component={About} />
</div>
</BrowserRouter>
一人以上の子供を考慮したよりきれいな方法
<div>
{ React.Children.map(this.props.children, child => React.cloneElement(child, {...this.props}))}
</div>
プロパティ転送を可能にする最良の方法は、関数のようにchildren
です。
例:
export const GrantParent = () => {
return (
<Parent>
{props => (
<ChildComponent {...props}>
Bla-bla-bla
</ChildComponent>
)}
</Parent>
)
}
export const Parent = ({ children }) => {
const somePropsHere = { //...any }
<>
{children(somePropsHere)}
</>
}
私はそれが this の代わりに this を使って動くようにするために上記の受け入れられた答えを修正する必要がありました。 これ map関数の範囲内で doSomething functionが定義されていません。
var Parent = React.createClass({
doSomething: function() {
console.log('doSomething!');
},
render: function() {
var that = this;
var childrenWithProps = React.Children.map(this.props.children, function(child) {
return React.cloneElement(child, { doSomething: that.doSomething });
});
return <div>{childrenWithProps}</div>
}})
更新:この修正はECMAScript 5用で、ES6では var that = this には必要ありません。
もしあなたが props に渡したい子供が複数いる場合は、React.Children.mapを使ってこのようにすることができます。
render() {
let updatedChildren = React.Children.map(this.props.children,
(child) => {
return React.cloneElement(child, { newProp: newProp });
});
return (
<div>
{ updatedChildren }
</div>
);
}
コンポーネントに子が1つだけの場合は、マッピングは不要です。ただちにcloneElementを使用するだけで済みます。
render() {
return (
<div>
{
React.cloneElement(this.props.children, {
newProp: newProp
})
}
</div>
);
}
回答のどれもがNOTである子を持つという問題に対処しません_テキストコンポーネントのようなReactコンポーネント回避策は次のようになります。
// Render method of Parent component
render(){
let props = {
setAlert : () => {alert("It works")}
};
let childrenWithProps = React.Children.map( this.props.children, function(child) {
if (React.isValidElement(child)){
return React.cloneElement(child, props);
}
return child;
});
return <div>{childrenWithProps}</div>
}
多くの人がこの機能をアンチパターンと見なしていますが、自分のしていることがわかっていてソリューションを適切に設計していればまだ使用できる場合もあります。
cloneElement()
のドキュメントによると
React.cloneElement(
element,
[props],
[...children]
)
開始点として要素を使用して、新しいReact要素を複製して返します。結果の要素は、元の要素の小道具と新しい小道具が浅くマージされたものになります。新しい子は既存の子を置き換えます。元の要素からのキーと参照は保持されます。
React.cloneElement()
は、ほぼ同等です。<element.type {...element.props} {...props}>{children}</element.type>
しかし、それはまたrefを保存します。これはつまり、あなたが子を参照の付いた子供にしても、それを先祖から誤って盗んだことにはならないということです。
そのため、cloneElementは、カスタムの小道具を子供に提供するために使用するものです。ただし、コンポーネント内に複数の子が存在する可能性があるため、それをループする必要があります。他の回答から示唆されるのは、あなたがReact.Children.map
を使ってそれらをマッピングすることです。ただし、React.Children.map
とは異なり、React.cloneElement
は要素の追加キーと追加の.$
を接頭辞として変更します。詳細については、この質問を確認してください。 React.Children.map内のReact.cloneElementによって要素キーが変更されている
それを避けたいのなら、代わりにforEach
関数を使うべきです。
render() {
const newElements = [];
React.Children.forEach(this.props.children,
child => newElements.Push(
React.cloneElement(
child,
{...this.props, ...customProps}
)
)
)
return (
<div>{newElements}</div>
)
}
私はレンダリングプロップがこのシナリオを処理するための適切な方法であると思います
次のように親コードをリファクタリングすることで、子コンポーネントで使用される必要な小道具を親に提供させます。
const Parent = ({children}) => {
const doSomething(value) => {}
return children({ doSomething })
}
それから子Componentで、あなたはこのように親によって提供される機能にアクセスすることができます:
class Child extends React {
onClick() => { this.props.doSomething }
render() {
return (<div onClick={this.onClick}></div>);
}
}
これで、婚約者の構造は次のようになります。
<Parent>
{(doSomething) =>
(<Fragment>
<Child value="1" doSomething={doSomething}>
<Child value="2" doSomething={doSomething}>
<Fragment />
)}
</Parent>
単一の子要素を持つ人なら誰でもこれをやるべきです。
{React.isValidElement(this.props.children)
? React.cloneElement(this.props.children, {
...prop_you_want_to_pass
})
: null}
Parent.jsx:
import React from 'react';
const doSomething = value => {};
const Parent = props => (
<div>
{
!props || !props.children
? <div>Loading... (required at least one child)</div>
: !props.children.length
? <props.children.type {...props.children.props} doSomething={doSomething} {...props}>{props.children}</props.children.type>
: props.children.map((child, key) =>
React.cloneElement(child, {...props, key, doSomething}))
}
</div>
);
Child.jsx:
import React from 'react';
/* but better import doSomething right here,
or use some flux store (for example redux library) */
export default ({ doSomething, value }) => (
<div onClick={() => doSomething(value)}/>
);
そしてmain.jsx:
import React from 'react';
import { render } from 'react-dom';
import Parent from './Parent';
import Child from './Child';
render(
<Parent>
<Child/>
<Child value='1'/>
<Child value='2'/>
</Parent>,
document.getElementById('...')
);
ここでの例を参照してください。 https://plnkr.co/edit/jJHQECrKRrtKlKYRpIWl?p=preview
@and_restの答えに加えて、これは私が子を複製してクラスを追加する方法です。
<div className="parent">
{React.Children.map(this.props.children, child => React.cloneElement(child, {className:'child'}))}
</div>
const Parent = (props) => {
const attributeToAddOrReplace= "Some Value"
const childrenWithAdjustedProps = React.Children.map(props.children, child =>
React.cloneElement(child, { attributeToAddOrReplace})
);
return <div>{childrenWithAdjustedProps }</div>
}
コンテキストを使用すると、間にあるコンポーネントを介してプロップとして明示的に渡すことなく、プロップを深い子コンポーネントに渡すことができます。
コンテキストには欠点があります:
構成可能なコンテキストを使用
export const Context = createContext<any>(null);
export const ComposableContext = ({ children, ...otherProps }:{children:ReactNode, [x:string]:any}) => {
const context = useContext(Context)
return(
<Context.Provider {...context} value={{...context, ...otherProps}}>{children}</Context.Provider>
);
}
function App() {
return (
<Provider1>
<Provider2>
<Displayer />
</Provider2>
</Provider1>
);
}
const Provider1 =({children}:{children:ReactNode}) => (
<ComposableContext greeting="Hello">{children}</ComposableContext>
)
const Provider2 =({children}:{children:ReactNode}) => (
<ComposableContext name="world">{children}</ComposableContext>
)
const Displayer = () => {
const context = useContext(Context);
return <div>{context.greeting}, {context.name}</div>;
};
これを行う最も簡単な方法:
{React.cloneElement(this.props.children, this.props)}
小道具をレンダリングする はこの問題に対する最も正確なアプローチです。子コンポーネントを子プロップとして親コンポーネントに渡す代わりに、親に子コンポーネントを手動でレンダリングさせます。 Render はreactに組み込まれた小道具で、関数のパラメータを取ります。この関数では、カスタムパラメータを使用して、親コンポーネントに好きなようにレンダリングさせることができます。基本的にそれは子供の小道具と同じことをしますが、それはよりカスタマイズ可能です。
class Child extends React.Component {
render() {
return <div className="Child">
Child
<p onClick={this.props.doSomething}>Click me</p>
{this.props.a}
</div>;
}
}
class Parent extends React.Component {
doSomething(){
alert("Parent talks");
}
render() {
return <div className="Parent">
Parent
{this.props.render({
anythingToPassChildren:1,
doSomething: this.doSomething})}
</div>;
}
}
class Application extends React.Component {
render() {
return <div>
<Parent render={
props => <Child {...props} />
}/>
</div>;
}
}
これはあなたが必要としたものですか?
var Parent = React.createClass({
doSomething: function(value) {
}
render: function() {
return <div>
<Child doSome={this.doSomething} />
</div>
}
})
var Child = React.createClass({
onClick:function() {
this.props.doSome(value); // doSomething is undefined
},
render: function() {
return <div onClick={this.onClick}></div>
}
})
どういうわけかReact.childrenが私のために働いていませんでした。これは私のために働いたものです。
子供にクラスを追加したいだけでした。支柱を変更するのと同じ
var newChildren = this.props.children.map((child) => {
const className = "MenuTooltip-item " + child.props.className;
return React.cloneElement(child, { className });
});
return <div>{newChildren}</div>;
ここでのトリックは React.cloneElement です。あなたは同じ方法でどんな小道具も渡すことができます