Przekaż rekwizyty komponentowi nadrzędnemu w React.js

295

Czy w React.js nie ma prostego sposobu na przekazanie dziecka propsrodzicowi za pomocą zdarzeń?

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 zestaw Kit n 'Kaboodle. Czasami komponent potomny zawiera zestaw informacji, których wolałbyś nie szukać.

Być może istnieje sposób na powiązanie komponentu ze zdarzeniem?

AKTUALIZACJA - 9/1/2015

Po ponad roku używania React i zachęceniu odpowiedzią Sebastiena Lorbera, doszedłem do wniosku, że przekazywanie elementów potomnych jako argumentów do funkcji w rodzicach nie jest w rzeczywistości sposobem React, ani też nie było dobrym pomysłem. Zmieniłem odpowiedź.

Kendall B.
źródło
Istnieją różne odpowiedzi na podobne problemy ( tu i tutaj ), ale żadne z nich nie wydaje się szczególnie eleganckie
Bojangles,
Zgadzam się - przekazywanie wydarzeń w górę łańcucha jest świetne, ale byłoby wspaniale wiedzieć na pewno, który składnik był inicjatorem wydarzenia.
KendallB,
Proszę spojrzeć na moje odpowiedzi, ponieważ uważam, że zaakceptowana odpowiedź nie jest wystarczająco dobra; stackoverflow.com/a/31756470/82609
Sebastien Lorber
1
kit n 'kaboodle - Aby uzyskać wszystko w zestawie. Aby zdobyć wszystkie dzwony i gwizdy. Combonation. urbandictionary.com/define.php?term=kit%20and%20caboodle
Filip Bartuzi

Odpowiedzi:

268

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

Ta odpowiedź dotyczy jedynie bezpośredniego związku rodzic-dziecko. Jeśli rodzic i dziecko mają potencjalnie wielu pośredników, sprawdź tę odpowiedź .

Inne rozwiązania nie mają sensu

Choć nadal działają dobrze, w innych odpowiedziach brakuje czegoś bardzo ważnego.

Czy w React.js nie ma prostego sposobu na przekazanie rekwizytów dziecka rodzicowi za pomocą zdarzeń?

Rodzic ma już tę dziecięcą rekwizyt! : jeśli dziecko ma rekwizyt, to dlatego, że jego rodzic dostarczył mu rekwizyt! Dlaczego chcesz, aby dziecko oddało rekwizyt rodzicowi, podczas gdy rodzic oczywiście już go ma?

Lepsze wdrożenie

Dziecko : 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 samotnym dzieckiem : używając wartości, którą przekazuje dziecku

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 od rodzica 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życiethis.state.childrenData[childIndex]

Uwaga: wiążemy się z nullkontekstem, ponieważ w przeciwnym razie React wydaje ostrzeżenie związane z systemem automatycznego wiązania . Użycie null oznacza, że ​​nie chcesz zmieniać kontekstu funkcji. Zobacz także .

O enkapsulacji i sprzęganiu w innych odpowiedziach

Jest to dla mnie zły pomysł pod względem łączenia 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śniono powyżej, masz już rekwizyty w rodzicu, więc nie ma sensu przekazywać całego komponentu potomnego, aby uzyskać dostęp do rekwizytów.

Korzystanie z referencji : w zdarzeniu masz już docelową liczbę kliknięć iw większości przypadków to wystarczy. Dodatkowo możesz użyć odwołania bezpośrednio do dziecka:

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

I uzyskaj dostęp do węzła DOM w obiekcie nadrzędnym za pomocą

React.findDOMNode(this.refs.theChild)

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

Komponent ma interfejs (rekwizyty), a rodzic nie powinien zakładać niczego o wewnętrznej pracy dziecka, w tym jego wewnętrznej strukturze DOM lub o których węzłach DOM deklaruje odniesienia. Rodzic korzystający z referencji dziecka oznacza, że ​​ściśle łączysz 2 elementy.

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

Stworzyli granicę między tym, co Ty, twórca stron internetowych może osiągnąć, a tym, co uważa się za szczegóły implementacji, dlatego są dla ciebie niedostępne. Przeglądarka może jednak dowolnie przemierzać tę granicę. Mając tę ​​granicę na miejscu, byli w stanie zbudować wszystkie elementy HTML przy użyciu tych samych dobrych starych technologii sieciowych, z div i rozpiętości, tak jak ty.

Problem polega na tym, że jeśli pozwolisz, aby szczegóły implementacji podrzędnej wyciekały do ​​elementu nadrzędnego, bardzo trudno jest refaktoryzować dziecko bez wpływu na element nadrzędny. Oznacza to, że jako autor biblioteki (lub jako edytor przeglądarki z Shadow DOM) jest to bardzo niebezpieczne, ponieważ pozwalasz klientowi na zbyt duży dostęp, co bardzo utrudnia aktualizację kodu bez zerwania kompatybilności.

Jeśli Chrome zaimplementował pasek przewijania umożliwiający klientowi dostęp do wewnętrznych węzłów domeny tego paska przewijania, oznacza to, że klient może mieć możliwość po prostu złamania tego paska przewijania, a aplikacje uległyby łatwiejszemu uszkodzeniu, gdy Chrome przeprowadzi automatyczną aktualizację po refaktoryzacji pasek przewijania ... 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

Przechodząc cały komponent w zwrotnego jest niebezpieczne i może prowadzić początkującym programistom zrobić bardzo dziwne rzeczy jak dzwoni childComponent.setState(...)lub childComponent.forceUpdate(), albo przypisując mu nowe zmienne wewnątrz rodzica, co czyni całą aplikację znacznie trudniej o przyczyny.


Edycja: przykłady ES6

Ponieważ wiele osób korzysta obecnie z 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ć klasą (i może ostatecznie zarządzać samym stanem, ale przekazuję go tutaj jako rekwizyty:

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


OSTRZEŻENIE PERF (dotyczy ES5 / ES6): jeśli używasz PureComponentlub shouldComponentUpdate, powyższe implementacje nie zostaną zoptymalizowane domyślnie, ponieważ używają onClick={e => doSomething()}lub wiążą się bezpośrednio podczas fazy renderowania, ponieważ utworzy nową funkcję za każdym razem, gdy renderuje rodzic. Jeśli jest to wąskie gardło w Twojej aplikacji, możesz przekazać dane dzieciom i ponownie wprowadzić je do „stabilnego” wywołania zwrotnego (ustawionego w klasie nadrzędnej i powiązanego z thiskonstruktorem klasy), aby PureComponentmożna było rozpocząć optymalizację lub może zaimplementować własne shouldComponentUpdatei zignorować wywołanie zwrotne podczas sprawdzania porównania rekwizytów.

Możesz także użyć biblioteki Przekomponuj , która zapewnia komponenty wyższego rzędu w celu uzyskania precyzyjnej optymalizacji:

// 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 takim przypadku możesz zoptymalizować komponent potomny, używając:

const OptimizedChild = onlyUpdateForKeys(['text'])(Child)
Sebastien Lorber
źródło
1
Tak, po spędzeniu ostatniego roku przy użyciu React, zgadzam się - nie ma żadnego powodu, aby przekazywać instancyjne komponenty za pośrednictwem funkcji. Zastąpię moją własną odpowiedź. Chcę również zmienić odpowiedź, z pewnymi modyfikacjami: twoje „Lepsze wdrożenie” nie odnosi się do faktycznego powodu, dla którego powstało to pytanie, a mianowicie: „Skąd mam wiedzieć, które z moich dzieci zostało wybrane z grupy?” Flux jest odpowiedzią na każdą aplikację o przyzwoitej wielkości, jednak w małych aplikacjach miło mi przekazać wartości (a nie komponenty), tworząc kopię zapasową łańcucha połączeń za pomocą funkcji props. Zgodzić się?
KendallB
@KendallB cieszę się, że zgadzasz się z moją odpowiedzią :) How do I know which of my children was chosen among a group?To nie jest tak naprawdę część pierwotnego pytania, ale załączyłem rozwiązanie do mojej odpowiedzi. W przeciwieństwie do twojej edycji, myślę, że wcale nie jest wymagana modyfikacja dziecka, nawet jeśli masz do czynienia z listą, ponieważ możesz go użyć bindbezpośrednio w rodzicu.
Sebastien Lorber
Zmodyfikowałem twoją odpowiedź, część dotyczącą referencji. omerwazir.com/posts/react-getdomnode-replaced-with-findDOMNode
ffxsam
Cześć @SebastienLorber Pochodzę z przyszłości. Odnosząc się do tej linii onClick={this.handleChildClick.bind(null,childData)}, o której wspominałeś o autobindowaniu Czy to nadal obowiązuje teraz, gdy React nie został uwzględniony w modelu klasowym . Przeczytałem wiele rzeczy, ale ta część wciąż mnie dezorientuje. Nawiasem class modelmówiąc, używam ES6 , więc zmieniłem nullna thisi mój kod nadal działa. Jak przetłumaczysz tę część kodu na styl ES6?
JohnnyQ,
Walczę też o to, by działało to w najnowszym React, co trzeba zmienić?
Hussein Duvigneau
143

Aktualizacja (9/1/15): PO sprawiło, że pytanie to stało się ruchomym celem. To zostało zaktualizowane ponownie. Czuję się więc odpowiedzialny za aktualizację mojej odpowiedzi.

Najpierw odpowiedź na podany przykład:

Tak, jest to możliwe.

Problem ten można rozwiązać poprzez aktualizację Dziecka onClickbyć this.props.onClick.bind(null, this):

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

Moduł obsługi zdarzeń w rodzicu może następnie uzyskać dostęp do komponentu i zdarzenia w następujący sposób:

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

Migawka JSBin


Ale samo pytanie jest mylące

Rodzic już zna dziecko props.

W podanym przykładzie nie jest to jasne, ponieważ w rzeczywistości nie ma żadnych rekwizytów. Ten przykładowy kod może lepiej obsługiwać 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ę o wiele wyraźniejsze, że wiesz już, jakie są rekwizyty Dziecka.

Migawka JSBin


Jeśli naprawdę chodzi o użycie rekwizytów dziecka…

Jeśli naprawdę chodzi o korzystanie z rekwizytów Dziecka, możesz całkowicie uniknąć połączenia z Dzieckiem.

JSX ma API atrybutów rozprzestrzeniania, którego często używam w komponentach takich jak Child. Pobiera wszystkie propsi stosuje je do komponentu. Dziecko wyglądałoby tak:

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

Umożliwiając korzystanie z wartości bezpośrednio w obiekcie nadrzędnym:

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

Migawka JSBin


Po podłączeniu dodatkowych komponentów potomnych nie jest wymagana dodatkowa konfiguracja

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 rzeczywisty przypadek użycia. Skopmy więc dalej…


Solidny praktyczny przykład

Trudno mówić o ogólnej naturze podanego przykładu. Stworzyłem komponent demonstrujący praktyczne zastosowanie powyższego pytania, zaimplementowany w bardzo Reacty sposób:

Przykład działania
DTServiceCalculator Repozytorium DTServiceCalculator

Ten element jest prostym kalkulatorem usług. Dostarczasz mu listę usług (z nazwami i cenami), a ona obliczy w sumie wybrane ceny.

Dzieci są błogo ignoranckie

ServiceItemjest elementem potomnym w tym przykładzie. Nie ma wielu opinii na temat świata zewnętrznego. To wymaga kilka rekwizytów , z których jeden jest funkcją nazywać po kliknięciu.

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

Nie robi nic poza wywołaniem podanego handleClickwywołania zwrotnego za pomocą podanego index[ źródła ].

Rodzice są dziećmi

DTServicesCalculatorjest elementem nadrzędnym w tym przykładzie. To także dziecko. Spójrzmy.

DTServiceCalculatortworzy listę elementów potomnych ServiceItemi dostarcza im rekwizyty [ źródło ]. Jest to komponent nadrzędny, ServiceItemale jest to komponent podrzędny komponentu, który przekazuje mu listę. Nie jest właścicielem danych. Dlatego ponownie przekazuje obsługę komponentu do źródła nadrzędnego

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

handleServiceItemprzechwytuje indeks przekazany od dziecka i przekazuje go swojemu rodzicowi [ źródło ]

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

Właściciele wiedzą wszystko

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

W pokazanym przykładzie kontynuuję delegowanie obsługi zdarzenia w górę drzewa komponentów, dopóki nie przejdziemy do komponentu, który jest właścicielem stanu.

Kiedy w końcu się tam dostaniemy, dokonujemy wyboru / cofnięcia wyboru 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 });
}


Wniosek

Staraj się, aby najbardziej zewnętrzne elementy były możliwie nieprzezroczyste. Staraj się upewnić, że mają bardzo mało preferencji dotyczących tego, w jaki sposób komponent nadrzędny może je zaimplementować.

Pamiętaj, kto jest właścicielem danych, którymi manipulujesz. W większości przypadków konieczne będzie przekazanie obsługi zdarzeń do drzewa do komponentu, który jest właścicielem tego stanu.

Poza tym: wzór Flux jest dobrym sposobem na ograniczenie tego rodzaju koniecznego łączenia w aplikacjach.

chantastyczny
źródło
Dzięki za zwięzłą odpowiedź, to naprawdę pomogło mi zrozumieć React nieco lepiej! Mam pytanie w tym samym stylu. Używam Flux do pub / sub. Zamiast przekazywać moduł obsługi zdarzeń podrzędnych do obiektu nadrzędnego, tak jak w twoim przykładzie, można zaimplementować to jako „akcję” i nasłuchiwać. Czy uznałbyś to za dobrą alternatywę i wykorzystanie Fluxa?
Pathsofdesign,
1
Dzięki @Pathsofdesign! To by zależało. Flux ma tę koncepcję widoków kontrolera. W tym przykładzie Parentmoże to być widok kontrolera, podczas gdy Childjest to widok głupi (komponent). Tylko widoki kontrolerów powinny mieć wiedzę o aplikacji. W takim przypadku nadal podajesz akcję od Parentdo Childjako rekwizyt. Jeśli chodzi o samą akcję, Flux ma ściśle określony wzorzec interakcji między akcjami w celu aktualizacji widoków. facebook.github.io/flux/docs/…
chantastic
1
onClick={this.onClick.bind(null, this.state.text)}nie ma potrzeby wiązania stanu, ponieważ rodzic ma to. więc po prostu onClick={this.onClick}zadziała. gdzie onClick = ()=>{const text = this.state.text;..}w rodzicu
Shishir Arora,
25

Wygląda na to, że istnieje 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} />
  }
});

Kluczem dzwoni bind(null, this)na this.props.onClickrazie minęło od rodzica. Teraz funkcja onClick akceptuje argumenty componentORAZ event. Myślę, że to najlepszy ze wszystkich światów.

AKTUALIZACJA: 9/1/2015

To był zły pomysł: ujawnienie rodzicom szczegółów implementacji dziecka nigdy nie było dobrą ścieżką. Zobacz odpowiedź Sebastiena Lorbera.

Kendall B.
źródło
czy argumenty nie są niewłaściwe (zamienione miejsca)?
The Surrican
1
@TheSurrican Nie, jest całkowicie w porządku. Pierwszy argument jest powiązany thisz funkcją (która i tak nie jest potrzebna), a drugi jest wstawiany jako pierwszy argument, gdy wywoływany jest onClick.
manmal
13

Pytanie brzmi, jak przekazać argument z elementu podrzędnego do elementu 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

rzymski
źródło
W moim przypadku zapomniałem przekazać funkcję zwrotną do click ()
Long Nguyen,
6

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

Dodając do wszystkich wspaniałych odpowiedzi, pozwól mi podać prosty przykład, który wyjaśnia przekazywanie wartości z elementu podrzędnego do elementu nadrzędnego w React

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

Header.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 z klienta na serwer.

Spójrz na funkcję Zmień w Header.js

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

W ten sposób wypychasz wartości do rekwizytów od klienta do serwera

Ignacy Andrzej
źródło
Czy mogę wiedzieć, w jaki sposób wypychasz wartości do rekwizytów od klienta do serwera, co masz na myśli przez to stwierdzenie
G1P
2

Oto prosta 3-etapowa implementacja ES6 z użyciem powiązania funkcji w konstruktorze nadrzędnym. Jest to pierwszy sposób, jaki zaleca oficjalny samouczek reagowania (nie omawiano tu również składni pól klasy publicznej). Wszystkie te informacje można znaleźć tutaj https://reactjs.org/docs/handling-events.html

Wiązanie funkcji rodzica, aby dzieci mogły do ​​nich zadzwonić (i przekazać dane rodzicowi!: D)

  1. Upewnij się, że w konstruktorze nadrzędnym powiązano funkcję utworzoną w obiekcie nadrzędnym
  2. Przekaż związaną funkcję do dziecka jako rekwizyt (bez lambda, ponieważ przekazujemy odwołanie do funkcji)
  3. Wywołaj funkcję powiązaną ze zdarzenia potomnego (Lambda! Wywołujemy tę funkcję, gdy zdarzenie zostanie uruchomione. Jeśli tego nie zrobimy, funkcja automatycznie uruchomi się podczas ładowania i nie zostanie uruchomiona w przypadku zdarzenia.)

Funkcja rodzica

handleFilterApply(filterVals){} 

Konstruktor macierzysty

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

Prop przekazany dziecku

onApplyClick = {this.handleFilterApply}

Wezwanie na imprezy dla dzieci

onClick = {() => {props.onApplyClick(filterVals)}
Jordan H.
źródło
0

To jest przykład bez użycia zdarzenia onClick. Po prostu przekazuję dziecku funkcję wywołania zwrotnego za pomocą rekwizytów. Dzięki temu wywołaniu zwrotnemu dziecko również odsyła dane. Zainspirowały mnie przykłady z dokumentów .

Mały przykład (jest w plikach tsx, więc rekwizyty i stany muszą być w pełni zadeklarowane, usunąłem trochę logiki ze składników, więc jest mniej kodu).

* Aktualizacja: Ważne jest, aby powiązać to z wywołaniem zwrotnym, w przeciwnym razie wywołanie zwrotne ma zasięg podrzędny, a nie nadrzędny. Jedyny problem: to „stary” rodzic…

SymptomChoser jest rodzicem:

interface SymptomChooserState {
  // true when a symptom was pressed can now add more detail
  isInDetailMode: boolean
  // since when user has this symptoms
  sinceDate: Date,
}

class SymptomChooser extends Component<{}, SymptomChooserState> {

  state = {
    isInDetailMode: false,
    sinceDate: new Date()
  }

  helloParent(symptom: Symptom) {
    console.log("This is parent of: ", symptom.props.name);
    // TODO enable detail mode
  }

  render() {
    return (
      <View>
        <Symptom name='Fieber' callback={this.helloParent.bind(this)} />
      </View>
    );
  }
}

Objawem jest dziecko (w rekwizytach dziecka zadeklarowałem funkcję wywołania zwrotnego, w wybranej funkcji Objaw jest wywoływany wywołanie zwrotne):

interface SymptomProps {
  // name of the symptom
  name: string,
  // callback to notify SymptomChooser about selected Symptom.
  callback: (symptom: Symptom) => void
}

class Symptom extends Component<SymptomProps, SymptomState>{

  state = {
    isSelected: false,
    severity: 0
  }

  selectedSymptom() {
    this.setState({ isSelected: true });
    this.props.callback(this);
  }

  render() {
    return (
      // symptom is not selected
      <Button
        style={[AppStyle.button]}
        onPress={this.selectedSymptom.bind(this)}>
        <Text style={[AppStyle.textButton]}>{this.props.name}</Text>
      </Button>
    );
  }
}
drewjosh
źródło