Jak przekazać rekwizyty {to.rekwizyty.dzieci}

Staram się znaleźć odpowiedni sposób, aby zdefiniować niektóre składniki, które mogą być używane w sposób ogólny:

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

Istnieje logika renderowania pomiędzy komponentami rodzica i dzieci oczywiście można sobie wyobrazić <select> i <option> jako przykład tej logiki.

Jest to fikcyjna implementacja do celów pytania:

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

Pytanie brzmi, Kiedy używasz {this.props.children} do zdefiniowania komponentu wrappera, jak przekazać jakąś właściwość do wszystkich jego dzieci?

Author: Tahir Ahmed, 2015-09-03

18 answers

Możesz użyć React.Dzieci aby iterować nad dziećmi, a następnie sklonować każdy element za pomocą nowych właściwości (shallow merged) za pomocą React.cloneElement np:

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

Https://jsfiddle.net/2q294y43/2/

 663
Author: Dominic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-07 18:47:08

Aby zrobić to nieco czystszym sposobem, spróbuj:

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

Notatka: będzie działać tylko wtedy, gdy istnieje pojedynczy element potomny i jest to poprawny element Reactowy.

 301
Author: Andres F Garcia,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-02 06:14:05

Spróbuj tego

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

Zadziałało u mnie przy użyciu Reacta-15.1.

 61
Author: 7puns,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-06 17:59:51

Przekaż rekwizyty dzieciom.

Zobacz wszystkie inne odpowiedzi

Przekazywanie współdzielonych, globalnych danych przez drzewo komponentów poprzez kontekst

Kontekst ma na celu udostępnianie danych, które można uznać za "globalne" dla drzewa komponentów Reactowych, takich jak aktualny uwierzytelniony użytkownik, motyw lub preferowany język. 1

zastrzeżenie: jest to zaktualizowana odpowiedź, poprzednia używała starego interfejsu API kontekstu

Opiera się na Zasada konsumenta / zapewnienia. Najpierw Utwórz swój kontekst

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

Następnie użyj via

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

I

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

Wszyscy konsumenci będący potomkami dostawcy będą ponownie renderować za każdym razem, gdy zmieni się wartość prop dostawcy. propagacja od dostawcy do potomka konsumentów nie podlega metodzie shouldComponentUpdate, więc konsument jest aktualizowany nawet wtedy, gdy przodek komponentu wycofuje się z aktualizacji. 1

Pełne przykład, semi-pseudo kod.

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

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

 39
Author: Lyubomir,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-25 14:18:56

Możesz użyć React.cloneElement, lepiej wiedzieć, jak to działa, zanim zaczniesz go używać w swojej aplikacji. Jest on wprowadzony w React v0.13, Czytaj dalej, aby uzyskać więcej informacji, więc coś wraz z tą pracą dla Ciebie:

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

Więc przynieś linie z Dokumentacji Reacta, aby zrozumieć, jak to wszystko działa i jak możesz z nich korzystać:

W Reaccie v0. 13 RC2 wprowadzimy nowe API, podobne do Reaguj.addons.clonewithprzypisy podpis:

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

W przeciwieństwie do cloneWithProps, ta nowa funkcja nie ma żadnej magii wbudowane zachowanie dla łączenia stylu i nazwy klasy z tego samego powodu nie mamy tej funkcji z transferPropsTo. Nikt nie jest pewien co dokładnie pełna lista magicznych rzeczy jest, co sprawia, że trudne do rozumowania o kodzie i trudne do ponownego użycia, gdy styl ma inny podpis (np. w nadchodzącym React Native).

React.cloneElement jest prawie odpowiednik:

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

Jednakże, w przeciwieństwie do JSX i cloneWithProps, zachowuje również refs. To oznacza, że jeśli dostaniesz dziecko z referentem, nie będziesz przypadkowo ukradnij to swojemu przodkowi. Otrzymasz ten sam ref dołączony do Twój nowy element.

Jednym z powszechnych wzorców jest mapowanie dzieci i dodawanie nowych rekwizytów. Zgłoszono wiele problemów z clonewithprzypisy, utrudniając rozumowanie o Twoim kodzie. Teraz po to samo wzór z cloneElement będzie działał zgodnie z oczekiwaniami. Na przykład:

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

Uwaga: Reaguj.cloneElement (child, { ref: 'newRef'}) zastępuje ref więc nadal nie jest możliwe, aby dwoje rodziców miało ref do to samo dziecko, chyba że użyjesz callback-refs.

To była krytyczna funkcja, aby dostać się do Reacta 0.13, ponieważ rekwizyty są teraz niezmienny. Ścieżka aktualizacji jest często do klonowania elementu, ale przez w ten sposób możesz stracić sędziego. Dlatego potrzebowaliśmy ładniejszy upgrade / align = "left" / W trakcie aktualizacji callsites na Facebook zdaliśmy sobie sprawę, że potrzebowaliśmy tej metody. Otrzymaliśmy te same opinie od społeczności. Dlatego postanowiliśmy zrobić kolejny RC przed finalnym wydaniem do upewnij się, że to wbiliśmy.

Planujemy ostatecznie zdeprecjonować React.addons.clonewithprzypisy Nie jesteśmy robi to jeszcze, ale jest to dobra okazja, aby zacząć myśleć o własne zastosowania i rozważ użycie Reacta.cloneElement zamiast. Będziemy na pewno do wysyłki wydanie z powiadomieniami o deprecjacji, zanim faktycznie usuń go, aby nie było potrzeby natychmiastowego działania.

Więcej tutaj ...

 16
Author: Alireza,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-27 12:49:59

Wraz z aktualizacją do React 16.3 możesz teraz używać React.createContext .

import * as React from 'react';

// React.createContext accepts a defaultValue as the first param
const { Provider: ParentProvider, Consumer: ChildConsumer } = React.createContext(); 

class Parent extends React.Component {
  doSomething = (value) => {
    // Do something here with value
  };

  render() {
    return (
       <ParentProvider value={{ onClick: this.doSomething }}>
         {this.props.children}
       </ParentProvider>
    );
  }
}

class Child extends React.Component {
  render() {
    const { value } = this.props;
    return (
       <ChildConsumer> 
         (({ doSomething }) => {
           return <div onClick={() => doSomething(value)}>{value}</div>
         })
       </ChildConsumer>
    );
  }
}


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

Reaguj.createContext świeci tam, gdzie reaguje.cloneElement case couldn ' t handle nested components

class SomeComponent extends React.Component {

  render() {
    <Parent>
      <Child value={1} />
      <SomeOtherComp><Child value={2} /></SomeOtherComp>
    </Parent>
  }
}
 15
Author: Kenneth Truong,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-21 10:59:37

Musiałem naprawić zaakceptowaną odpowiedź powyżej, aby działała za pomocą that zamiast this pointer. Ten w ramach funkcji map nie miał zdefiniowanej funkcji doSomething .

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

Aktualizacja: ta poprawka jest dla ECMAScript 5, W ES6 nie ma potrzeby w var that = this

 5
Author: olenak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-04 18:33:46

Cleaner way considering one or more children

<div>
   { React.Children.map(this.props.children, child => React.cloneElement(child, {...this.props}))}
</div>
 5
Author: and_rest,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-02 00:07:23

Nie potrzebujesz już {this.props.children}. Teraz możesz zawinąć komponent potomny używając render w Route i przekazać rekwizyty jak zwykle:

<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>
 4
Author: yeasayer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-02 08:40:07

Żadna z odpowiedzi nie rozwiązuje problemu posiadania dzieci, które są a nie komponentami Reactowymi, takimi jak ciągi tekstowe. Obejście może być czymś takim:

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

}
 3
Author: poynting,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-16 14:07:58

Jeśli masz wiele dzieci, którym chcesz przekazać właściwości, możesz to zrobić w ten sposób, używając Reacta.Dzieci.Mapa:

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

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

Jeśli twój komponent ma tylko jedno dziecko, nie ma potrzeby mapowania, możesz po prostu kloneelement od razu:

render() {
    return (
        <div>
            {
                React.cloneElement(this.props.children, {
                    newProp: newProp
                })
            }
        </div>
    );
}
 2
Author: Nesha Zoric,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-01 15:12:18

Dalej do odpowiedzi @ and_rest, tak klonuję dzieci i dodaję klasę.

<div className="parent">
    {React.Children.map(this.props.children, child => React.cloneElement(child, {className:'child'}))}
</div>
 1
Author: sidonaldson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-02 11:18:58

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

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

Zobacz przykład tutaj: https://plnkr.co/edit/jJHQECrKRrtKlKYRpIWl?p=preview

 1
Author: Maksim Kostromin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-17 13:52:35

Zgodnie z dokumentacją cloneElement()

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

Klonowanie i zwracanie nowego elementu Reactowego za pomocą elementu startowego punkt. Wynikowy element będzie miał właściwości oryginalnego elementu z nowymi rekwizytami połączonymi płytko. Nowe dzieci zastąpią istniejące dzieci. klucz i ref z oryginalnego elementu będą zachowany.

React.cloneElement() jest prawie równoważne:

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

Jednak zachowuje również ref. Oznacza to, że jeśli otrzymasz dziecko z sędzią, nie ukradniesz go przypadkowo swojemu przodkowi. Otrzymasz ten sam ref dołączony do nowego elementu.

Więc cloneElement jest tym, czego byś użył, aby zapewnić dzieciom niestandardowe rekwizyty. Jednak w komponencie może być wiele dzieci i trzeba będzie go zapętlić. Inne odpowiedzi sugerują, że możesz je odwzorować za pomocą React.Children.map. Jednak React.Children.map W przeciwieństwie do React.cloneElement zmienia klucze elementu dodającego i extra .$ jako prefiks. Sprawdź to pytanie, aby uzyskać więcej szczegółów: React.cloneElement inside React.Dzieci.Mapa powoduje zmianę kluczy elementów

Jeśli chcesz tego uniknąć, powinieneś zamiast tego wybrać forEach funkcję podobną do

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

}
 1
Author: Shubham Khatri,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-11 05:57:17

Być może Możesz również znaleźć przydatną funkcję, choć wiele osób uznało to za anty-wzorzec, który nadal może być używany, jeśli wiesz, co robisz i dobrze zaprojektujesz swoje rozwiązanie.

Function as Child Components

 1
Author: Alexandr Cherednichenko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-10 21:19:35

Czy tego potrzebowałeś?

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
Author: amit_183,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-03 09:02:02

Najładniejszy sposób na to:

    {React.cloneElement(this.props.children, this.props)}
 0
Author: nitte93user3232918,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-06 06:31:16

Jakiś powód reaguje.dzieci nie pracowały dla mnie. To mi się udało.

Chciałam dodać dziecku klasę. podobne do zmiana prop
 var newChildren = this.props.children.map((child) => {
 const className = "MenuTooltip-item " + child.props.className;
    return React.cloneElement(child, { className });
 });

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

Sztuczka polega na tym, że reaguje.cloneElement . Możesz przekazać dowolny rekwizyt w podobny sposób

 0
Author: aWebDeveloper,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-27 08:54:51