Przekazuje właściwości do komponentu nadrzędnego w Reaccie.js

Czy nie ma prostego sposobu przekazania dziecka props rodzicowi za pomocą zdarzeń w Reaccie.js?

var Child = React.createClass({
  render: function() {
    <a onClick={this.props.onClick}>Click me</a>
  }
});

var Parent = React.createClass({
  onClick: function(event) {
    // event.component.props ?why is this not available?
  },
  render: function() {
    <Child onClick={this.onClick} />
  }
});

Wiem, że możesz użyć kontrolowanych komponentów, aby przekazać wartość wejściową, ale byłoby miło przekazać cały kit n ' kaboodle. Czasami komponent potomny zawiera zestaw informacji, których nie trzeba szukać.

Może istnieje sposób, aby powiązać komponent ze zdarzeniem?

Aktualizacja-9/1/2015

Po użyciu Reacta przez ponad rok i Odpowiedź Sebastiena Lorbera, doszedłem do wniosku, że przekazywanie komponentów potomnych jako argumentów do funkcji w rodzicach jest , a nie W rzeczywistości sposobem reakcji, ani też nie był to dobry pomysł. Zmieniłem odpowiedź.

Author: KendallB, 2014-03-25

6 answers

Edit : zobacz przykłady końcowe dla zaktualizowanych przykładów ES6.

Ta odpowiedź po prostu zajmuje się sprawą bezpośredniej relacji rodzic-dziecko. Jeśli rodzic i dziecko mają potencjalnie wielu pośredników, sprawdź tę odpowiedź .

Innych rozwiązań brakuje punktu

Chociaż nadal działają dobrze, Inne odpowiedzi brakuje czegoś bardzo ważnego.

Czy nie ma prostego sposobu na przekazanie dziecku rekwizytów rodzicowi za pomocą zdarzeń, w reakcji.js?

Rodzic ma już ten dziecięcy rekwizyt!: jeśli dziecko ma rekwizyt, to dlatego, że jego rodzic dostarczył go dziecku! Dlaczego chcesz, aby dziecko przekazało rekwizyt rodzicowi, podczas gdy rodzic oczywiście ma już ten rekwizyt?

Lepsza implementacja

Child: to naprawdę nie musi być bardziej skomplikowane.

var Child = React.createClass({
  render: function () {
    return <button onClick={this.props.onClick}>{this.props.text}</button>;
  },
});

Rodzic z pojedynczym dzieckiem: używając wartości, którą przekazuje do dziecko

var Parent = React.createClass({
  getInitialState: function() {
     return {childText: "Click me! (parent prop)"};
  },
  render: function () {
    return (
      <Child onClick={this.handleChildClick} text={this.state.childText}/>
    );
  },
  handleChildClick: function(event) {
     // You can access the prop you pass to the children 
     // because you already have it! 
     // Here you have it in state but it could also be
     //  in props, coming from another parent.
     alert("The Child button text is: " + this.state.childText);
     // You can also access the target of the click here 
     // if you want to do some magic stuff
     alert("The Child HTML is: " + event.target.outerHTML);
  }
});

JsFiddle

Rodzic z listą dzieci: nadal masz wszystko, czego potrzebujesz na rodzicu i nie musisz komplikować dziecka.

var Parent = React.createClass({
  getInitialState: function() {
     return {childrenData: [
         {childText: "Click me 1!", childNumber: 1},
         {childText: "Click me 2!", childNumber: 2}
     ]};
  },
  render: function () {
    var children = this.state.childrenData.map(function(childData,childIndex) {
        return <Child onClick={this.handleChildClick.bind(null,childData)} text={childData.childText}/>;
    }.bind(this));
    return <div>{children}</div>;
  },

  handleChildClick: function(childData,event) {
     alert("The Child button data is: " + childData.childText + " - " + childData.childNumber);
     alert("The Child HTML is: " + event.target.outerHTML);
  }
});

JsFiddle

Możliwe jest również użycie this.handleChildClick.bind(null,childIndex), a następnie użycie this.state.childrenData[childIndex]

Uwaga wiążemy się z kontekstem null, ponieważ w przeciwnym razie React wysyła ostrzeżenie związane ze swoim systemem autobinding . Używanie null oznacza, że nie chcesz zmieniać kontekst funkcji. Zobacz też .

O enkapsulacji i sprzężeniu w innych odpowiedziach

To jest dla mnie zły pomysł w zakresie sprzężenia i hermetyzacji:

var Parent = React.createClass({
  handleClick: function(childComponent) {
     // using childComponent.props
     // using childComponent.refs.button
     // or anything else using childComponent
  },
  render: function() {
    <Child onClick={this.handleClick} />
  }
});

Używanie rekwizytów : Jak wyjaśniłem powyżej, masz już rekwizyty w rodzicu, więc bezużyteczne jest przekazywanie całego komponentu potomnego do dostępu do rekwizytów.

Korzystanie z refs : Masz już cel kliknięcia w przypadku i w większości przypadków wystarczy. Dodatkowo można było użyć ref bezpośrednio na dziecku:

<Child ref="theChild" .../>

I dostęp do węzła DOM w rodzicu za pomocą

React.findDOMNode(this.refs.theChild)

W bardziej zaawansowanych przypadkach, gdy chcesz uzyskać dostęp do wielu referencji dziecka w rodzicu, dziecko może przekazać wszystkie węzły dom bezpośrednio w wywołaniu zwrotnym.

Komponent ma interfejs (właściwości) i rodzic nie powinien zakładać nic o wewnętrznej pracy dziecka, w tym jego wewnętrznej struktury DOM lub które węzły DOM deklaruje refy za. Rodzic używający ref dziecka oznacza, że ciasno połączysz 2 składniki.

Aby zilustrować problem, wezmę ten cytat o Shadow DOM , który jest używany w przeglądarkach do renderowania rzeczy takich jak suwaki, paski przewijania, odtwarzacze wideo...:

Stworzyli granicę między tym, co Ty, web developer może osiągnąć i co jest uważane za szczegóły realizacji, a więc niedostępne dla ty. Przeglądarka może jednak przechodzić przez to granica do woli. Z tą granicą byli w stanie zbudować wszystkie elementy HTML korzystanie z tych samych, starych, dobrych technologii internetowych, z Div I tak jak ty.

Problem polega na tym, że jeśli pozwolisz, aby szczegóły implementacji dziecka wyciekły do rodzica, bardzo utrudniasz refaktoryzację dziecka bez wpływu na rodzica. Oznacza to, że jako autor biblioteki (lub edytor przeglądarki Z Shadow DOM) jest to bardzo niebezpieczne, ponieważ pozwalasz klientowi również uzyskać dostęp wiele, co sprawia, że bardzo trudno jest uaktualnić kod bez łamania wstecznej zgodności.

Jeśli Chrome zaimplementował swój scrollbar, pozwalając klientowi uzyskać dostęp do wewnętrznych węzłów dom tego scrollbara, oznacza to, że klient może mieć możliwość po prostu złamania tego scrollbara, a aplikacje łatwiej się zepsują, gdy Chrome wykona automatyczną aktualizację po refaktoryzacji scrollbara... Zamiast tego dają dostęp tylko do niektórych bezpiecznych rzeczy, takich jak dostosowywanie niektórych części paska przewijania za pomocą CSS.

O używaniu czegokolwiek innego

Przekazanie całego komponentu w wywołaniu zwrotnym jest niebezpieczne i może skłonić początkujących programistów do zrobienia bardzo dziwnych rzeczy, takich jak wywołanie childComponent.setState(...) lub childComponent.forceUpdate() lub przypisanie mu nowych zmiennych wewnątrz rodzica, co znacznie utrudnia rozumowanie całej aplikacji.


Edit: ES6 examples

Ponieważ wiele osób używa teraz ES6, oto te same przykłady składni ES6

Dziecko może być bardzo proste:

const Child = ({
  onClick, 
  text
}) => (
  <button onClick={onClick}>
    {text}
  </button>
)
Rodzic może być albo klasą (i może w końcu zarządzać samym stanem, ale przekazuję go jako rekwizyty tutaj:]}
class Parent1 extends React.Component {
  handleChildClick(childData,event) {
     alert("The Child button data is: " + childData.childText + " - " + childData.childNumber);
     alert("The Child HTML is: " + event.target.outerHTML);
  }
  render() {
    return (
      <div>
        {this.props.childrenData.map(child => (
          <Child
            key={child.childNumber}
            text={child.childText} 
            onClick={e => this.handleChildClick(child,e)}
          />
        ))}
      </div>
    );
  }
}

Ale można go również uprościć, jeśli nie trzeba zarządzać stanem:

const Parent2 = ({childrenData}) => (
  <div>
     {childrenData.map(child => (
       <Child
         key={child.childNumber}
         text={child.childText} 
         onClick={e => {
            alert("The Child button data is: " + child.childText + " - " + child.childNumber);
                    alert("The Child HTML is: " + e.target.outerHTML);
         }}
       />
     ))}
  </div>
)

JsFiddle


Uwaga PERF (dotyczy ES5/ES6): jeśli używasz PureComponent lub shouldComponentUpdate, powyższe implementacje nie będą domyślnie optymalizowane, ponieważ używasz onClick={e => doSomething()} lub wiązania bezpośrednio w fazie renderowania, ponieważ tworzy nową funkcję za każdym razem, gdy rodzic renderuje. Jeśli jest to wąskie gardło perf w Twojej aplikacji, możesz przekazać dane potomkom i ponownie umieścić je w "stabilnym" wywołaniu zwrotnym (ustawionym na klasie nadrzędnej i przypisanym do this w konstruktorze klasy), tak aby optymalizacja PureComponent mogła zacząć działać, lub możesz zaimplementować własne shouldComponentUpdate i zignorować wywołanie zwrotne w sprawdzaniu porównań właściwości.

Możesz również użyć Przekomponować bibliotekę, która dostarcza komponenty wyższego rzędu do osiągnij dopracowane optymalizacje:

// A component that is expensive to render
const ExpensiveComponent = ({ propA, propB }) => {...}

// Optimized version of same component, using shallow comparison of props
// Same effect as React's PureRenderMixin
const OptimizedComponent = pure(ExpensiveComponent)

// Even more optimized: only updates if specific prop keys have changed
const HyperOptimizedComponent = onlyUpdateForKeys(['propA', 'propB'])(ExpensiveComponent)

W tym przypadku można zoptymalizować komponent potomny za pomocą:

const OptimizedChild = onlyUpdateForKeys(['text'])(Child)
 218
Author: Sebastien Lorber,
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-05-23 11:55:01

Aktualizacja (9/1/15): OP uczynił to pytanie nieco ruchomym celem. Został ponownie zaktualizowany. Czuję się więc odpowiedzialny za zaktualizowanie mojej odpowiedzi.

Najpierw odpowiedź na podany przez Ciebie przykład:

Tak, to możliwe.

Możesz to rozwiązać, aktualizując onClick dziecka Na this.props.onClick.bind(null, this):

var Child = React.createClass({
  render: function () {
    return <a onClick={this.props.onClick.bind(null, this)}>Click me</a>;
  }
});

Obsługa zdarzenia w Rodzicu może wtedy uzyskać dostęp do komponentu i zdarzenia w następujący sposób:

  onClick: function (component, event) {
    // console.log(component, event);
  },

JSBin migawka


Ale samo pytanie jest mylące.]}

Rodzic zna już dziecko props.

Nie jest to jasne w podanym przykładzie, ponieważ żadne rekwizyty nie są faktycznie dostarczane. Ten przykładowy kod może lepiej wspierać zadawane pytanie: {]}

var Child = React.createClass({
  render: function () {
    return <a onClick={this.props.onClick}> {this.props.text} </a>;
  }
});

var Parent = React.createClass({
  getInitialState: function () {
    return { text: "Click here" };
  },
  onClick: function (event) {
    // event.component.props ?why is this not available? 
  },
  render: function() {
    return <Child onClick={this.onClick} text={this.state.text} />;
  }
});

W tym przykładzie staje się znacznie jaśniejsze, że już wiesz, jakie są rekwizyty dziecka.

JSBin migawka


Jeśli naprawdę chodzi o używanie dziecięcych rekwizytów...]}

Jeśli naprawdę chodzi o używanie dziecięcych rekwizytów, możesz w ogóle uniknąć kontaktu z dzieckiem.

JSX ma spread atrybuty API, których często używam na komponentach takich jak Child. Pobiera wszystkie props i stosuje je do komponentu. Dziecko wyglądałoby tak:

var Child = React.createClass({
  render: function () {
    return <a {...this.props}> {this.props.text} </a>;
  }
});

Pozwala na użycie wartości bezpośrednio w Rodzicu:

var Parent = React.createClass({
  getInitialState: function () {
    return { text: "Click here" };
  },
  onClick: function (text) {
    alert(text);
  },
  render: function() {
    return <Child onClick={this.onClick.bind(null, this.state.text)} text={this.state.text} />;
  }
});

JSBin migawka


i nie jest wymagana żadna dodatkowa konfiguracja, ponieważ podłączasz dodatkowe komponenty potomne

var Parent = React.createClass({
  getInitialState: function () {
    return {
      text: "Click here",
      text2: "No, Click here",
    };
  },
  onClick: function (text) {
    alert(text);
  },
  render: function() {
    return <div>
      <Child onClick={this.onClick.bind(null, this.state.text)} text={this.state.text} />
      <Child onClick={this.onClick.bind(null, this.state.text2)} text={this.state.text2} />
    </div>;
  }
});

Migawka JSBin

Ale podejrzewam, że to nie jest Twój przypadek użycia. Więc kopmy dalej ...


Solidny praktyczny przykład

Ogólny charakter podanego przykładu jest trudny do omówienia. Stworzyłem komponent, który demonstruje praktyczne zastosowanie w powyższym pytaniu, zaimplementowane w bardzo Reacty sposób:

Przykład pracy DTServiceCalculator
dtservicecalculator repo

Ten komponent jest prostym kalkulatorem usług. Podasz mu listę usług (z nazwami i cenami), a on obliczy łącznie wybrane ceny.

Dzieci są błogimi ignorantami

ServiceItem jest komponentem potomnym w tym przykładzie. Nie ma wielu opinii na temat świata zewnętrznego. To wymaga kilku rekwizytów , z których jeden jest funkcją do wywołania po kliknięciu.

<div onClick={this.props.handleClick.bind(this.props.index)} />

Nie robi nic poza wywołaniem dostarczonego handleClick callback z dostarczonym index[źródło].

Rodzice to dzieci

DTServicesCalculator czy komponent nadrzędny jest w tym przykładzie. To także dziecko. Zobaczmy.

DTServiceCalculator tworzy listę komponentów potomnych (ServiceItems) i dostarcza im właściwości [source]. To rodzic-komponent ServiceItem, ale jest to komponent potomny komponentu przekazujący mu listę. Nie posiada danych. Tak więc ponownie deleguje obsługę komponentu do jego komponentu macierzystego źródło

<ServiceItem chosen={chosen} index={i} key={id} price={price} name={name} onSelect={this.props.handleServiceItem} />

handleServiceItem zapisuje indeks, przekazywany od dziecka i przekazuje go rodzicowi [źródło ]

handleServiceClick (index) {
  this.props.onSelect(index);
}

Właściciele wiedzą wszystko

Pojęcie" własności " jest ważne w Reaccie. Polecam przeczytać więcej na ten temat tutaj .

W przykładzie, który pokazałem, przekazuję obsługę zdarzenia w górę drzewa komponentów, dopóki nie dotrzemy do komponentu, który jest właścicielem stanu.

Kiedy w końcu tam dotrzemy, zajmiemy się wyborem/usunięciem stanu w ten sposób [źródło]:

handleSelect (index) {
  let services = […this.state.services];
  services[index].chosen = (services[index].chosen) ? false : true;
  this.setState({ services: services });
}


Podsumowanie

Staraj się utrzymać większość zewnętrznych elementów tak nieprzezroczystych, jak to tylko możliwe. Staraj się upewnić, że mają bardzo mało preferencji co do tego, jak komponent nadrzędny może je wdrożyć.

Bądź świadomy, kto jest właścicielem danych, którymi manipulujesz. W większości przypadków trzeba będzie delegować obsługę zdarzeń w drzewie do komponentu, który jest właścicielem tego stanu.

pomijając: wzór strumienia jest dobrym sposobem na zmniejszenie tego typu niezbędnego podłączenia w aplikacjach.

 140
Author: chantastic,
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-09-13 11:08:40

Wygląda na to, że jest prosta odpowiedź. Rozważ to:

var Child = React.createClass({
  render: function() {
    <a onClick={this.props.onClick.bind(null, this)}>Click me</a>
  }
});

var Parent = React.createClass({
  onClick: function(component, event) {
    component.props // #=> {Object...}
  },
  render: function() {
    <Child onClick={this.onClick} />
  }
});

Klucz wywołuje bind(null, this) w zdarzeniu this.props.onClick, przekazanym od rodzica. Teraz funkcja onClick przyjmuje argumenty component i event. Myślę, że to najlepsze ze wszystkich światów.

Aktualizacja: 9/1/2015

To był zły pomysł: przekazanie rodzicowi szczegółów implementacji dziecka nigdy nie było dobrą drogą. Zobacz odpowiedź Sebastiena Lorbera.

 24
Author: KendallB,
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-08-01 15:09:27

Pytanie brzmi, jak przekazać argument z komponentu podrzędnego do komponentu nadrzędnego. Ten przykład jest łatwy w użyciu i przetestowany:

//Child component
class Child extends React.Component {
    render() {
        var handleToUpdate  =   this.props.handleToUpdate;
        return (<div><button onClick={() => handleToUpdate('someVar')}>Push me</button></div>
        )
    }
}

//Parent component
class Parent extends React.Component {
        constructor(props) {
        super(props);
        var handleToUpdate  = this.handleToUpdate.bind(this);
    }

        handleToUpdate(someArg){
            alert('We pass argument from Child to Parent: \n' + someArg);
    }

    render() {
        var handleToUpdate  =   this.handleToUpdate;
        return (<div>
                    <Child handleToUpdate = {handleToUpdate.bind(this)} /></div>)
    }
}

if(document.querySelector("#demo")){
    ReactDOM.render(
        <Parent />,
        document.querySelector("#demo")
    );
}

Spójrz na JSFIDDLE

 7
Author: Roman,
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 08:09:41

Zasadniczo używasz rekwizytów do wysyłania informacji do i od dziecka i rodzica.

Dodając do wszystkich wspaniałych odpowiedzi, podam prosty przykład, który wyjaśnia przekazywanie wartości z komponentu podrzędnego do komponentu nadrzędnego w Reaccie

App.js

class App extends React.Component {
      constructor(){
            super();
            this.handleFilterUpdate = this.handleFilterUpdate.bind(this);
            this.state={name:'igi'}
      }
      handleFilterUpdate(filterValue) {
            this.setState({
                  name: filterValue
            });
      }
   render() {
      return (
        <div>
            <Header change={this.handleFilterUpdate} name={this.state.name} />
            <p>{this.state.name}</p>
        </div>
      );
   }
}

Nagłówek.js

class Header extends React.Component {
      constructor(){
            super();
            this.state={
                  names: 'jessy'
            }
      }
      Change(event) {

      // this.props.change(this.state.names);
      this.props.change('jessy');
  }

   render() {
      return (
       <button onClick={this.Change.bind(this)}>click</button>

      );
   }
}

Main.js

import React from 'react';
import ReactDOM from 'react-dom';

import App from './App.jsx';

ReactDOM.render(<App />, document.getElementById('app'));

To wszystko , teraz możesz przekazywać wartości ze swojego klienta do serwera.

Spójrz na funkcję zmiany w Nagłówek.js

Change(event) {
      // this.props.change(this.state.names);
      this.props.change('jessy');
  }

W ten sposób wpychasz wartości do Właściwości z Klienta na Serwer

 5
Author: Ignatius Andrew,
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-01 12:00:47

Oto prosta 3-stopniowa implementacja ES6 wykorzystująca powiązanie funkcji w konstruktorze nadrzędnym. Jest to pierwszy sposób zalecany przez oficjalny samouczek Reacta(nie ma tu również składni pól klasy publicznej). Wszystkie te informacje znajdziesz tutaj https://reactjs.org/docs/handling-events.html

Wiążące funkcje rodzica, aby dzieci mogły je wywoływać (i przekazywać dane do rodzica! : D)

  1. upewnij się, że w konstruktorze nadrzędnym powiązałeś funkcję utworzone w rodzicu
  2. przekazać funkcję bound w dół do dziecka jako prop (Nie lambda, ponieważ przekazujemy ref do funkcji)
  3. wywołanie funkcji bound Z zdarzenia potomnego (Lambda! Wywołujemy funkcję po wywołaniu zdarzenia. Jeśli tego nie zrobimy, funkcja uruchomi się automatycznie po załadowaniu i nie zostanie uruchomiona podczas zdarzenia.)

Funkcja Rodzica

handleFilterApply(filterVals){} 

Konstruktor Rodzica

this.handleFilterApply = this.handleFilterApply.bind(this);

Prop przeszedł do Dziecko

onApplyClick = {this.handleFilterApply}

Wywołanie Zdarzenia Potomnego

onClick = {() => {props.onApplyClick(filterVals)}
 1
Author: Jordan H,
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-09 20:48:33