it-swarm.com.de

So übergeben Sie Requisiten an {this.props.children}

Ich versuche den richtigen Weg zu finden, um einige Komponenten zu definieren, die generisch verwendet werden könnten:

<Parent>
  <Child value="1">
  <Child value="2">
</Parent>

Es gibt natürlich eine Logik für das Rendern zwischen übergeordneten und untergeordneten Komponenten. Sie können sich <select> und <option> als Beispiel für diese Logik vorstellen.

Dies ist eine Dummy-Implementierung im Sinne der Frage:

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

Die Frage ist, wann immer Sie {this.props.children} zum Definieren einer Wrapper-Komponente verwenden. Wie geben Sie eine Eigenschaft an alle untergeordneten Objekte weiter?

682
plus-

Sie können React.Children verwenden, um über die Kinder zu iterieren, und dann jedes Element mit neuen Requisiten (oberflächlich zusammengefügt) mit React.cloneElement z. B. klonen.

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

Geige: https://jsfiddle.net/2q294y43/2/

756
Dominic

Versuchen Sie Folgendes, um es etwas sauberer zu machen:

<div>
    {React.cloneElement(this.props.children, { loggedIn: this.state.loggedIn })}
</div>

Hinweis : Dies funktioniert nur, wenn ein einzelnes Kind vorhanden ist und es ein gültiges React-Element ist.

318
Andres F Garcia

Versuche dies

<div>{React.cloneElement(this.props.children, {...this.props})}</div>

Es funktionierte für mich mit dem Einsatz von reag-15.1.

64
7puns

Übergeben Sie Requisiten, um Kinder zu leiten.

Alle anderen Antworten anzeigen

Übergeben Sie gemeinsam genutzte globale Daten über den Komponentenbaum über context

Kontext ist für die Freigabe von Daten vorgesehen, die als "global" für einen Baum von React-Komponenten betrachtet werden können, beispielsweise den aktuellen authentifizierten Benutzer, das Design oder die bevorzugte Sprache. 1

Haftungsausschluss: Dies ist eine aktualisierte Antwort, die vorherige verwendete die alte Kontext-API

Es basiert auf dem Consumer/Provide-Prinzip. Erstellen Sie zuerst Ihren Kontext 

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

Dann nutzen Sie via

<Provider value={/* some value */}>
  {children} /* potential consumers */
<Provider />

und 

<Consumer>
  {value => /* render something based on the context value */}
</Consumer>

Alle Verbraucher, die Nachkommen eines Anbieters sind, werden erneut gerendert, wenn sich die Werthaltigkeit des Anbieters ändert. Die Weitergabe von Provider an untergeordnete Consumer unterliegt nicht der sollteComponentUpdate-Methode. Daher wird der Consumer auch dann aktualisiert, wenn eine Vorfahrkomponente die Aktualisierung beendet. 

Vollständiges Beispiel, Semi-Pseudo-Code.

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

1 https://facebook.github.io/react/docs/context.html

49
Lyubomir

Mit dem Update auf React 16.6 können Sie jetzt React.createContext und contextType verwenden. 

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 scheint, wo React.cloneElement case verschachtelte Komponenten nicht verarbeiten konnte

class SomeComponent extends React.Component {

  render() {
    <Parent>
      <Child value={1} />
      <SomeOtherComp><Child value={2} /></SomeOtherComp>
    </Parent>
  }
}
30
Kenneth Truong

Sie können React.cloneElement verwenden. Es ist besser zu wissen, wie es funktioniert, bevor Sie es in Ihrer Anwendung verwenden. Es wurde in React v0.13 eingeführt. Lesen Sie weiter, um weitere Informationen zu erhalten.

<div>{React.cloneElement(this.props.children, {...this.props})}</div>

Nehmen Sie die Zeilen aus der React-Dokumentation auf, damit Sie wissen, wie alles funktioniert und wie Sie davon Gebrauch machen können:

In React v0.13 RC2 stellen wir eine neue API vor, ähnlich wie React.addons.cloneWithProps mit dieser Signatur:

React.cloneElement(element, props, ...children);

Im Gegensatz zu cloneWithProps besitzt diese neue Funktion keine Magie integriertes Verhalten zum Zusammenführen von style und className aus demselben Grund Wir haben diese Funktion von transferPropsTo nicht. Niemand weiß genau was. genau die vollständige Liste der magischen Dinge ist, was es macht Es ist schwierig, den Code zu begründen, und er kann nur schwer wiederverwendet werden. hat eine andere Signatur (z. B. beim anstehenden React Native).

React.cloneElement ist fast äquivalent zu:

<element.type {...element.props} {...props}>{children}</element.type>

Im Gegensatz zu JSX und cloneWithProps bleiben jedoch auch Refs erhalten. Diese bedeutet, dass wenn Sie ein Kind mit einem Ref darauf bekommen, Sie nicht aus Versehen stehlen Sie es von Ihrem Vorfahren. Sie erhalten dasselbe Ref. An dein neues Element.

Ein übliches Muster ist es, Ihre Kinder zu kartieren und eine neue Requisite hinzuzufügen. Es wurden viele Probleme gemeldet, dass cloneWithProps den Ref. .__ verloren hatte. es ist schwieriger, über den Code nachzudenken. Nun folgt das gleiche Muster mit CloneElement funktioniert wie erwartet. Zum Beispiel:

var newChildren = React.Children.map(this.props.children, function(child) {
  return React.cloneElement(child, { foo: true })
});

Hinweis: React.cloneElement (untergeordnetes Element, {ref: 'newRef'}) überschreibt das ref so ist es immer noch nicht möglich, dass zwei eltern einen ref an die dasselbe Kind, es sei denn, Sie verwenden Callback-Refs.

Dies war ein kritischer Aspekt, um in React 0.13 einzusteigen, da Requisiten jetzt .__ sind. unveränderlich. Der Aktualisierungspfad besteht oft darin, das Element zu klonen, jedoch mit Wenn Sie dies tun, verlieren Sie möglicherweise den Hinweis. Daher brauchten wir ein besseres Upgrade Weg hier. Als wir bei Facebook ein Upgrade vorgenommen haben, haben wir festgestellt, dass Wir brauchten diese Methode. Wir haben das gleiche Feedback von der Community bekommen . Daher haben wir uns entschlossen, vor der endgültigen Veröffentlichung eine weitere RC auf .__ zu erstellen. Stellen Sie sicher, dass wir dies in bekommen.

Wir haben vor, React.addons.cloneWithProps irgendwann abzulehnen. Waren nicht es noch nicht, aber dies ist eine gute Gelegenheit, über .__ nachzudenken. Ihre eigenen Verwendungen und erwägen Sie stattdessen die Verwendung von React.cloneElement. Wir werden sicher, eine Version mit veralteten Benachrichtigungen zu versenden, bevor wir tatsächlich entfernen Sie es, so dass keine sofortige Aktion erforderlich ist.

mehr hier ...

18
Alireza

Sie brauchen {this.props.children} nicht mehr. Jetzt können Sie Ihre untergeordnete Komponente mit render in Route einpacken und wie üblich Ihre Requisiten übergeben:

<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>
5
yeasayer

Sauberere Art, ein oder mehrere Kinder zu berücksichtigen

<div>
   { React.Children.map(this.props.children, child => React.cloneElement(child, {...this.props}))}
</div>
4
and_rest

Der beste Weg, die Eigenschaftsübertragung zu ermöglichen, ist children wie eine Funktion

Beispiel:

export const GrantParent = () => {
  return (
    <Parent>
      {props => (
        <ChildComponent {...props}>
          Bla-bla-bla
        </ChildComponent>
      )}
    </Parent>
  )
}

export const Parent = ({ children }) => {
    const somePropsHere = { //...any }
    <>
        {children(somePropsHere)}
    </>
}
4

Ich musste die akzeptierte Antwort oben korrigieren, damit sie mit that anstelle von this pointer funktioniert. Für diese im Rahmen der Kartenfunktion wurde keine doSomething - Funktion definiert.

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>
}})

Update: Dieses Update ist für ECMAScript 5, in ES6 ist in var keine Notwendigkeit, dass = this

4
olenak

Wenn Sie mehrere Kinder haben, die Sie Requisiten übergeben wollen , können Sie dies mit der React.Children.map tun:

render() {
    let updatedChildren = React.Children.map(this.props.children,
        (child) => {
            return React.cloneElement(child, { newProp: newProp });
        });

    return (
        <div>
            { updatedChildren }
        </div>
    );
}

Wenn Ihre Komponente nur ein Kind hat, ist kein Mapping erforderlich. Sie können das Element einfach klonen:

render() {
    return (
        <div>
            {
                React.cloneElement(this.props.children, {
                    newProp: newProp
                })
            }
        </div>
    );
}
2
Nesha Zoric

Keine der Antworten befasst sich mit der Frage, ob es sich um Kinder handelt, dieNICHTReact-Komponenten sind, wie z. B. Textzeichenfolgen. Eine Problemumgehung könnte etwa so aussehen:

// 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>

}
2
poynting

Vielleicht können Sie diese Funktion auch als nützlich erachten, obwohl viele Leute dies als Anti-Pattern betrachtet haben. Sie kann jedoch immer noch verwendet werden, wenn Sie wissen, was Sie tun und Ihre Lösung gut entwerfen.

Funktion als untergeordnete Komponente

Laut der Dokumentation von cloneElement()

React.cloneElement(
  element,
  [props],
  [...children]
)

Klonen Sie und geben Sie ein neues React-Element zurück. Verwenden Sie element als Start Punkt. Das resultierende Element enthält die Requisiten des ursprünglichen Elements mit den neuen Requisiten flach eingegossen. Neue Kinder ersetzen bestehende Kinder. key und ref vom ursprünglichen Element lautet konserviert.

React.cloneElement() ist fast äquivalent zu:

<element.type {...element.props} {...props}>{children}</element.type>

Es behält jedoch auch Refs. Dies bedeutet, wenn Sie ein Kind bekommen Mit einem Verweis darauf stehlen Sie es nicht versehentlich von Ihrem Vorfahren. Sie erhalten den gleichen Ref zu Ihrem neuen Element.

CloneElement ist also das, was Sie verwenden würden, um den Kindern benutzerdefinierte Requisiten bereitzustellen. Es können jedoch mehrere untergeordnete Elemente in der Komponente vorhanden sein, und Sie müssen darüber eine Schleife machen. Was andere Antworten vorschlagen, ist, dass Sie sie mit React.Children.map überzeichnen. Im Gegensatz zu React.Children.map ändert React.cloneElement jedoch die Schlüssel des anhängenden Elements und den zusätzlichen .$ als Präfix. Weitere Informationen finden Sie in dieser Frage: React.cloneElement in React.Children.map bewirkt, dass sich Elementschlüssel ändern

Wenn Sie es vermeiden möchten, sollten Sie stattdessen die Funktion forEach wählen

render() {
    const newElements = [];
    React.Children.forEach(this.props.children, 
              child => newElements.Push(
                 React.cloneElement(
                   child, 
                   {...this.props, ...customProps}
                )
              )
    )
    return (
        <div>{newElements}</div>
    )

}
2
Shubham Khatri

Ich denke, eine Render-Requisite ist der geeignete Weg, um mit diesem Szenario umzugehen

Sie lassen das übergeordnete Element die erforderlichen Requisiten für die untergeordnete Komponente bereitstellen, indem Sie den übergeordneten Code so umgestalten, dass er ungefähr so ​​aussieht:

const Parent = ({children}) => {
  const doSomething(value) => {}

  return children({ doSomething })
}

In der untergeordneten Komponente können Sie dann folgendermaßen auf die vom übergeordneten Element bereitgestellte Funktion zugreifen:

class Child extends React {

  onClick() => { this.props.doSomething }

  render() { 
    return (<div onClick={this.onClick}></div>);
  }

}

Nun sieht die Fianl-Struktur so aus:

<Parent>
  {(doSomething) =>
   (<Fragment>
     <Child value="1" doSomething={doSomething}>
     <Child value="2" doSomething={doSomething}>
    <Fragment />
   )}
</Parent>
1
Ze Rubeus

Für jeden, der ein einzelnes untergeordnetes Element hat, sollte dies gemacht werden.

{React.isValidElement(this.props.children)
                  ? React.cloneElement(this.props.children, {
                      ...prop_you_want_to_pass
                    })
                  : null}
1
user10884362

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)}/>
);

und 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('...')
);

beispiel hier: https://plnkr.co/edit/jJHQECrKRrtKlKYRpIWl?p=preview

1

Neben @und_rest antworte ich auf diese Weise, wie ich die Kinder klone und eine Klasse hinzufüge.

<div className="parent">
    {React.Children.map(this.props.children, child => React.cloneElement(child, {className:'child'}))}
</div>
1
sidonaldson

Methode 1 - Kinder klonen

const Parent = (props) => {
   const attributeToAddOrReplace= "Some Value"
   const childrenWithAdjustedProps = React.Children.map(props.children, child =>
      React.cloneElement(child, { attributeToAddOrReplace})
   );

   return <div>{childrenWithAdjustedProps }</div>
}

Methode 2 - Verwenden Sie zusammensetzbaren Kontext

Mit Context können Sie eine Requisite an eine untergeordnete Komponente übergeben, ohne sie explizit als Requisite durch die dazwischen liegenden Komponenten zu übergeben.

Der Kontext bringt Nachteile mit sich:

  1. Daten fließen nicht regelmäßig - über Requisiten.
  2. Durch die Verwendung des Kontexts entsteht ein Vertrag zwischen dem Verbraucher und dem Anbieter. Es ist möglicherweise schwieriger, die Anforderungen zu verstehen und zu replizieren, die für die Wiederverwendung einer Komponente erforderlich sind.

nter Verwendung eines zusammensetzbaren Kontexts

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>;
};

1
Ben Carp

Der glattste Weg, dies zu tun:

    {React.cloneElement(this.props.children, this.props)}
0

Render Requisiten ist der genaueste Ansatz für dieses Problem. Lassen Sie die untergeordnete Komponente nicht als untergeordnete Elemente an die übergeordnete Komponente übergeben, sondern lassen Sie die untergeordnete Komponente manuell rendern. Render ist eine eingebaute Requisite in reag, die Funktionsparameter übernimmt. In dieser Funktion können Sie die übergeordnete Komponente mit benutzerdefinierten Parametern rendern lassen. Im Grunde macht es dasselbe wie Kinderrequisiten, ist aber anpassbarer. 

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>;
  }
}

Beispiel bei codepen

0
omeralper

Hast du das gefordert? 

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>
  }
})
0
amit_183

Ein Grund, warum React.children nicht für mich gearbeitet hat. Das hat bei mir funktioniert.

Ich wollte dem Kind nur eine Klasse hinzufügen. ähnlich einem Requisitenwechsel

 var newChildren = this.props.children.map((child) => {
 const className = "MenuTooltip-item " + child.props.className;
    return React.cloneElement(child, { className });
 });

 return <div>{newChildren}</div>;

Der Trick hier ist das React.cloneElement . Sie können jede Requisite auf ähnliche Weise übergeben

0
aWebDeveloper