Wie man Requisiten an {this.props.children} weitergibt


888

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 eine Logik für die Darstellung zwischen Eltern und Kindern Komponenten natürlich geht, können Sie sich vorstellen , <select>und <option>als ein Beispiel dieser Logik.

Dies ist eine Dummy-Implementierung für den Zweck 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}eine Wrapper-Komponente definieren, wie Sie eine Eigenschaft an alle untergeordneten Elemente weitergeben.


Antworten:


955

Kinder mit neuen Requisiten klonen

Sie können React.Children verwenden , um die untergeordneten Elemente zu durchlaufen , und dann jedes Element mit neuen Requisiten (flach zusammengeführt) mit React.cloneElement klonen, z.

import React, { Children, isValidElement, cloneElement } from 'react';

const Child = ({ doSomething, value }) => (
  <div onClick={() => doSomething(value)}>Click Me</div>
);

function Parent({ children }) {
  function doSomething(value) {
    console.log('doSomething called by child with value:', value);
  }

  render() {
    const childrenWithProps = Children.map(children, child => {
      // Checking isValidElement is the safe way and avoids a TS error too.
      if (isValidElement(child)) {
        return cloneElement(child, { doSomething })
      }

      return child;
    });

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

ReactDOM.render(
  <Parent>
    <Child value="1" />
    <Child value="2" />
  </Parent>,
  document.getElementById('container')
);

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

Kinder als Funktion anrufen

Sie können Requisiten auch an Kinder mit Render-Requisiten übergeben . Bei diesem Ansatz sind die childrenuntergeordneten Elemente (die ein beliebiger Requisitenname sein können) eine Funktion, die alle Argumente akzeptieren kann, die Sie übergeben möchten, und die untergeordneten Elemente zurückgibt:

const Child = ({ doSomething, value }) => (
  <div onClick={() =>  doSomething(value)}>Click Me</div>
);

function Parent({ children }) {
  function doSomething(value) {
    console.log('doSomething called by child with value:', value);
  }

  render() {
    // Note that children is called as a function and we can pass args to it
    return <div>{children(doSomething)}</div>
  }
};

ReactDOM.render(
  <Parent>
    {doSomething => (
      <React.Fragment>
        <Child doSomething={doSomething} value="1" />
        <Child doSomething={doSomething} value="2" />
      </React.Fragment>
    )}
  </Parent>,
  document.getElementById('container')
);

Anstelle von <React.Fragment>oder einfach können <>Sie auch ein Array zurückgeben, wenn Sie dies bevorzugen.

Geige: https://jsfiddle.net/ferahl/y5pcua68/7/


7
Das funktioniert bei mir nicht. Dies ist nicht in React.cloneElement () definiert
Patrick

12
Diese Antwort funktioniert nicht, die valueWeitergabe doSomethinggeht verloren.
Dave

3
@DominicTobias Arg, sorry, ich habe console.log auf Alert umgestellt und vergessen, die beiden Parameter auf eine einzige Zeichenfolge zu konzentrieren.
Dave

1
Diese Antwort war sehr hilfreich, aber ich bin auf ein Problem gestoßen, das hier nicht erwähnt wird, und habe mich gefragt, ob es eine neue Sache ist, die sich geändert hat, oder ob es etwas Seltsames für mich ist. Als ich mein untergeordnetes Element geklont habe, wurde das untergeordnete Element auf das alte Element gesetzt, bis ich this.props.children.props.children zum dritten Argument von cloneElement hinzugefügt habe.
Aphenin

7
Was ist, wenn das Kind über eine Route (v4) geladen wird, die von einer separaten Routenseite geladen wird?
Blamb

394

Versuchen Sie für eine etwas sauberere Vorgehensweise:

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

Bearbeiten: Zur Verwendung mit mehreren einzelnen Kindern (das Kind muss selbst eine Komponente sein) können Sie dies tun. Getestet in 16.8.6

<div>
    {React.cloneElement(props.children[0], { loggedIn: true, testingTwo: true })}
    {React.cloneElement(props.children[1], { loggedIn: true, testProp: false })}
</div>

10
Ich habe die am besten bewertete Antwort verwendet, aber diese ist viel einfacher! Diese Lösung wird auch auf der Seite mit den Beispielen für den React-Router verwendet.
captDaylight

10
Könnte jemand erklären, wie das funktioniert (oder was es tatsächlich tut)? Beim Lesen der Dokumente konnte ich nicht sehen, wie dies zu den Kindern führen und jedem Kind diese Requisite hinzufügen würde - ist es das, was es tun soll? Wenn ja, woher wissen wir, dass dies der Fall ist? Es ist überhaupt nicht offensichtlich, dass es sogar gültig ist, eine undurchsichtige Datenstruktur ( this.props.children) an cloneElement... zu übergeben, die ein ... Element erwartet.
GreenAsJade

51
Genau das scheint bei mehr als einem Kind nicht zu funktionieren.
Danita

17
Sie können also Code schreiben, der funktioniert, während jemand nur ein Kind an eine Komponente übergibt, aber wenn er ein anderes hinzufügt, stürzt er ab ... das klingt auf den ersten Blick nicht gut? Es scheint eine Falle für das OP zu sein, das speziell nach der Weitergabe von Requisiten an alle Kinder fragte .
GreenAsJade

10
@ GreenAsJade ist in Ordnung, solange Ihre Komponente ein einzelnes Kind erwartet. Sie können über Ihre Komponenten propTypes definieren, dass ein einzelnes Kind erwartet wird. React.Children.onlyDie Funktion gibt das einzige untergeordnete Element zurück oder löst eine Ausnahme aus, wenn mehrere vorhanden sind (dies wäre nicht vorhanden, wenn es keinen Anwendungsfall gäbe).
Cchamberlain

80

Versuche dies

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

Es hat bei mir mit react-15.1 funktioniert.


3
Ist es möglich, React.cloneElement()direkt zurückzukehren, ohne es in <div>Tags zu umgeben? Denn was ist, wenn das Kind ein <span>(oder etwas anderes) ist und wir seinen Tag-Elementtyp beibehalten möchten?
Adrianmc

1
Wenn es ein Kind ist, können Sie den Wrapper weglassen und diese Lösung funktioniert nur für ein Kind, also ja.
ThaJay

1
Funktioniert bei mir. Ohne Beilage ist <div> in Ordnung.
Crash Override

4
Wenn Sie explizit erzwingen müssen, dass Sie nur ein Kind erhalten, können Sie React.cloneElement(React.Children.only(this.props.children), {...this.props})einen Fehler auslösen, wenn mehr als ein Kind übergeben wird. Dann müssen Sie sich nicht in ein Div einwickeln.
Itsananderson

1
Diese Antwort kann einen TypError: zyklischen Objektwert erzeugen. Verwenden Sie let {children, ...acyclicalProps} = this.propsund dann, es sei denn, Sie möchten, dass eine der Requisiten des Kindes sich selbst ist React.cloneElement(React.Children.only(children), acyclicalProps).
Parabolord

68

Requisiten an direkte Kinder weitergeben.

Alle anderen Antworten anzeigen

Übergeben Sie gemeinsam genutzte globale Daten über den Kontext durch den Komponentenbaum

Der Kontext dient zum Teilen von Daten, die für einen Baum von React-Komponenten als "global" betrachtet werden können, z. B. der aktuell authentifizierte Benutzer, das Thema oder die bevorzugte Sprache. 1

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

Es basiert auf dem Consumer / Provide-Prinzip. Erstellen Sie zunächst Ihren Kontext

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

Dann benutze 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 Wertstütze des Anbieters ändert. Die Weitergabe von Provider an seine Nachkommen Consumers unterliegt nicht der shouldComponentUpdate-Methode, sodass der Consumer auch dann aktualisiert wird, wenn eine Vorgängerkomponente aus dem Update ausscheidet. 1

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


6
Im Gegensatz zur akzeptierten Antwort funktioniert dies auch dann ordnungsgemäß, wenn unter Parent andere Elemente enthalten sind. Dies ist definitiv die beste Antwort.
Zaptree

6
Requisiten! = Kontext
Petr Peller

Sie können sich nicht auf Änderungen verlassen, die sich durch den Kontext verbreiten. Verwenden Sie Requisiten, wenn es möglich ist, dass sie sich ändern.
ThaJay

1
Vielleicht verstehe ich es nicht, aber ist es nicht falsch zu sagen, dass "Kontext Requisiten verfügbar macht"? Als ich den Kontext das letzte Mal verwendet habe, war es eine separate Sache (dh this.context) - es hat den Kontext nicht auf magische Weise mit Requisiten zusammengeführt. Sie mussten absichtlich den Kontext festlegen und verwenden, was eine ganz andere Sache ist.
Josh

Sie verstehen perfekt, es war falsch. Ich habe meine Antwort bearbeitet.
Lyubomir

48

Requisiten an verschachtelte Kinder weitergeben

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() {
    return (
      <Parent>
        <Child value={1} />
        <Child value={2} />
      </Parent>
    );
  }
}

React.createContext leuchtet dort, wo der Fall React.cloneElement verschachtelte Komponenten nicht verarbeiten konnte

class SomeComponent extends React.Component {

  render() {
    return (
      <Parent>
        <Child value={1} />
        <SomeOtherComp><Child value={2} /></SomeOtherComp>
      </Parent>
    );
  }
}

3
Können Sie erklären, warum => Funktionen eine schlechte Praxis sind? Die => Funktion hilft, die Ereignishandler zu binden, um thisKontext zu erhalten
Kenneth Truong

@KennethTruong, da bei jedem Rendern eine Funktion erstellt wird.
funktioniert nicht

9
@itdoesntwork das ist nicht wahr. Es wird nur eine neue Funktion erstellt, wenn die Klasse erstellt wird. Es wird nicht während der Renderfunktion erstellt.
Kenneth Truong

@KennethTruong reactjs.org/docs/faq-functions.html#arrow-function-in-render Ich dachte, Sie sprechen über die Pfeilfunktion beim Rendern.
funktioniert nicht

24

Sie können verwenden React.cloneElement, es ist besser zu wissen, wie es funktioniert, bevor Sie es in Ihrer Anwendung verwenden. Es wird in eingeführt React v0.13, lesen Sie weiter für weitere Informationen, also etwas zusammen mit dieser Arbeit für Sie:

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

Bringen Sie also die Zeilen aus der React-Dokumentation mit, damit Sie verstehen, wie alles funktioniert und wie Sie sie verwenden können:

In React v0.13 RC2 werden wir eine neue API einführen, ähnlich wie React.addons.cloneWithProps, mit dieser Signatur:

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

Im Gegensatz zu cloneWithProps verfügt diese neue Funktion über kein integriertes magisches Verhalten zum Zusammenführen von Stil und Klassenname, aus dem gleichen Grund, aus dem wir diese Funktion von transferPropsTo nicht haben. Niemand ist sich sicher, was genau die vollständige Liste der magischen Dinge ist, was es schwierig macht, über den Code nachzudenken und ihn wiederzuverwenden, wenn der Stil eine andere Signatur hat (z. B. im kommenden React Native).

React.cloneElement entspricht fast:

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

Im Gegensatz zu JSX und cloneWithProps werden jedoch auch refs beibehalten. Das heißt, wenn Sie ein Kind mit einem Schiedsrichter haben, werden Sie es nicht versehentlich Ihrem Vorfahren stehlen. Sie erhalten den gleichen Verweis an Ihr neues Element.

Ein häufiges Muster besteht darin, Ihre Kinder abzubilden und eine neue Requisite hinzuzufügen. Es wurden viele Probleme gemeldet, bei denen cloneWithProps den Ref verloren hat, was es schwieriger macht, über Ihren Code nachzudenken. Wenn Sie nun mit cloneElement dem gleichen Muster folgen, funktioniert dies wie erwartet. Zum Beispiel:

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

Hinweis: React.cloneElement (child, {ref: 'newRef'}) überschreibt den Ref, sodass zwei Eltern immer noch keinen Ref für dasselbe Kind haben können, es sei denn, Sie verwenden Callback-Refs.

Dies war eine wichtige Funktion für React 0.13, da Requisiten jetzt unveränderlich sind. Der Upgrade-Pfad besteht häufig darin, das Element zu klonen. Andernfalls verlieren Sie möglicherweise die Referenz. Deshalb brauchten wir hier einen schöneren Upgrade-Pfad. Als wir Callsites bei Facebook aktualisierten, stellten wir fest, dass wir diese Methode brauchten. Wir haben das gleiche Feedback von der Community erhalten. Aus diesem Grund haben wir uns entschlossen, vor der endgültigen Veröffentlichung einen weiteren RC zu erstellen, um sicherzustellen, dass wir diesen erhalten.

Wir planen, React.addons.cloneWithProps irgendwann zu verwerfen. Wir tun es noch nicht, aber dies ist eine gute Gelegenheit, über Ihre eigenen Verwendungszwecke nachzudenken und stattdessen React.cloneElement zu verwenden. Wir werden sicher sein, eine Version mit Verfallsbenachrichtigungen zu versenden, bevor wir sie tatsächlich entfernen, sodass keine sofortigen Maßnahmen erforderlich sind.

mehr hier ...


18

Der beste Weg, um Eigentum zu übertragen, ist childrenwie 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)}
    </>
}

1
Dies scheint mir viel einfacher (und besser für die Leistung?) Zu sein als die akzeptierte Antwort.
Shikyo

2
Dies erfordert, dass Kinder eine Funktion sind und funktioniert nicht für verschachtelte Komponenten
digitale Illusion

@ Digitalitalusion, ich verstehe nicht, was es bedeutet nested components. React hat keine verschachtelten Muster, sondern nur Kompositionen. Ja, Kinder müssen eine Funktion sein, es gibt keine Konflikte, da dies ein gültiges JSX-Kind ist. Können Sie ein Beispiel geben mit nesting components?
Nick Ovchinnikov

1
Sie haben Recht, der Fall von tief verschachtelten Kindern kann auch behandelt werden, <Parent>{props => <Nest><ChildComponent /></Nest>}</Parent>anstatt (nicht zu arbeiten), <Parent><Nest>{props => <ChildComponent />}</Nest></Parent>also stimme ich zu, dass dies die beste Antwort ist
digitale Illusion

Beim Versuch erhalte ich Folgendes:TypeError: children is not a function
Ryan Prentiss

6

Ich musste die oben akzeptierte Antwort korrigieren, damit sie mit dieser anstelle dieses Zeigers funktioniert . Für diese Funktion wurde im Rahmen der Kartenfunktion die Funktion doSomething nicht 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: Dieser Fix ist für ECMAScript 5, in ES6 ist var that = this nicht erforderlich


13
oder verwenden Sie einfachbind()
plus-

1
Oder verwenden Sie eine
Dominic

Was wäre, wenn Sie doSomethingein Objekt nehmen würden, wie doSomething: function(obj) { console.log(obj) }und in dem Kind, das Sie anrufen würden this.props.doSomething(obj), um sich "obj"
abzumelden

4
@ plus- Ich weiß, dass dies alt ist, aber die Verwendung von bind hier ist eine schreckliche Idee. bind erstellt eine neue Funktion, die den Kontext an eine neue bindet. im Grunde eine Funktion, die die applyMethode aufruft . Die Verwendung bind()in der Renderfunktion erstellt bei jedem Aufruf der Rendermethode eine neue Funktion.
Bamieh

6

Sauberer Weg, wenn man ein oder mehrere Kinder berücksichtigt

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

Dieser funktioniert bei mir nicht, er gibt einen Fehler: Kinder nicht definiert.
Deelux

@Deelux this.props.children statt Kinder
Martin Dawson

Dies gibt das Kind als seine eigenen Kinder in this.props. Im Allgemeinen würde ich nur empfehlen, mit bestimmten Requisiten zu klonen, nicht mit dem ganzen Shebang.
Andy

Das Bestehen {...this.props}hat bei mir nicht funktioniert, ist der Weg {...child.props}richtig?
Felipe Augusto

Für Funktionskomponenten:React.Children.map(children, child => React.cloneElement(child, props))
vsync

5

Keine der Antworten befasst sich mit dem Problem, Kinder zu haben, die KEINE Reaktionskomponenten sind, wie z. B. Textzeichenfolgen. Eine Problemumgehung könnte ungefähr 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>

}

5

Du brauchst nicht mehr {this.props.children}. Jetzt können Sie Ihre untergeordnete Komponente mit renderin einwickeln Routeund Ihre Requisiten wie gewohnt weitergeben:

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

2
Render-Requisiten sind jetzt Standard in React ( reactjs.org/docs/render-props.html ) und sollten als neue akzeptierte Antwort auf diese Frage in Betracht gezogen werden.
Ian Danforth

19
Wie ist das eine Antwort auf die Frage?
Maximo Dominguez

4

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

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



4

Wenn Sie mehrere Kinder haben, an die Sie Requisiten übergeben möchten , können Sie dies auf folgende Weise mithilfe 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 untergeordnetes Element hat, ist keine Zuordnung erforderlich. Sie können Element einfach sofort klonen:

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

3

Nach der Dokumentation von cloneElement()

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

Klonen Sie ein neues React-Element und geben Sie es mit dem Element als Ausgangspunkt zurück. Das resultierende Element enthält die Requisiten des ursprünglichen Elements, wobei die neuen Requisiten flach zusammengeführt werden. Neue Kinder werden bestehende Kinder ersetzen. Schlüssel und Referenz des ursprünglichen Elements bleiben erhalten.

React.cloneElement() ist fast gleichbedeutend mit:

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

Es bewahrt jedoch auch refs. Das heißt, wenn Sie ein Kind mit einem Schiedsrichter haben, werden Sie es nicht versehentlich Ihrem Vorfahren stehlen. Sie erhalten den gleichen Verweis an Ihr neues Element.

CloneElement ist also das, was Sie verwenden würden, um den Kindern benutzerdefinierte Requisiten bereitzustellen. Die Komponente kann jedoch mehrere untergeordnete Elemente enthalten, die Sie durchlaufen müssen. Andere Antworten schlagen vor, dass Sie sie mit abbilden React.Children.map. Im React.Children.mapGegensatz zu React.cloneElementÄnderungen werden jedoch die Schlüssel des Elements angehängt und .$als Präfix hinzugefügt. Überprüfen Sie diese Frage auf weitere Details: React.cloneElement in React.Children.map bewirkt, dass sich Elementschlüssel ändern

Wenn Sie es vermeiden möchten, sollten Sie stattdessen die forEachFunktion wie 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

Nach der Antwort von @and_rest klone ich die Kinder auf diese Weise und füge eine Klasse hinzu.

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

2

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

Sie lassen den Elternteil die erforderlichen Requisiten bereitstellen, die in der untergeordneten Komponente verwendet werden, indem Sie den übergeordneten Code so umgestalten, dass er wie folgt aussieht:

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

  return children({ doSomething })
}

Dann können Sie in der untergeordneten Komponente auf folgende Weise auf die vom übergeordneten Element bereitgestellte Funktion zugreifen:

class Child extends React {

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

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

}

Jetzt sieht die Verlobungsstruktur folgendermaßen aus:

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

Was ist, wenn das übergeordnete Element nur ein Wrapper für ein {Kinder} ist, das ein Textbereich in einer anderen Klassenkomponente ist?
Adebayo

2

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 einen 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 hat Nachteile:

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

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

Ein bisschen spät, aber könnten Sie die Notation in erklären {children}:{children:ReactNode}?
Camille

@camille, es ist eine Typoskript-Sache. Wenn ich es mir jetzt anschaue, würde ich nur mit Javascript antworten, und selbst wenn ich Typescript schreiben würde, würde ich es anders machen. Könnte es in Zukunft bearbeiten.
Ben Carp

1
@camille, im Grunde bedeutet es, dass der Wert, der den Schlüssel "children"hat, vom Typ istReactNode
Ben Carp

1

Der einfachste Weg, dies zu tun:

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

5
Kopiert dies nicht this.props.children in this.props.children des Kindes? und tatsächlich das Kind in sich selbst kopieren?
Arshabh Agarwal

1

Für jeden, der ein einzelnes untergeordnetes Element hat, sollte dies der Fall sein.

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

0

Ist es das, was Sie benötigt haben?

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

4
Nein, ich möchte den Inhalt meines Wrappers nicht auf einen bestimmten Inhalt beschränken.
Plus-

0

Aus irgendeinem Grund hat React.children nicht für mich gearbeitet. Das hat bei mir funktioniert.

Ich wollte dem Kind nur eine Klasse hinzufügen. ähnlich wie beim Wechseln einer Requisite

 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

Render Requisiten ist der genaueste Ansatz für dieses Problem. Anstatt die untergeordnete Komponente als untergeordnete Requisiten an die übergeordnete Komponente zu übergeben, lassen Sie die untergeordnete Komponente manuell übergeordnete Komponente rendern. Render ist eingebaute Requisiten in reagieren, die Funktionsparameter nehmen. In dieser Funktion können Sie die übergeordnete Komponente mit benutzerdefinierten Parametern beliebig rendern lassen. Grundsätzlich funktioniert es genauso wie Kinderrequisiten, ist jedoch 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

Bei Verwendung von Funktionskomponenten wird häufig der TypeError: Cannot add property myNewProp, object is not extensibleFehler angezeigt, wenn Sie versuchen, neue Eigenschaften festzulegen props.children. Dies wird umgangen, indem die Requisiten geklont und dann das Kind selbst mit den neuen Requisiten geklont werden.

const MyParentComponent = (props) => {
  return (
    <div className='whatever'>
      {props.children.map((child) => {
        const newProps = { ...child.props }
        // set new props here on newProps
        newProps.myNewProp = 'something'
        const preparedChild = { ...child, props: newProps }
        return preparedChild
      })}
    </div>
  )
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.