Czy w React.js nie ma prostego sposobu na przekazanie dziecka props
rodzicowi 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ź.
javascript
reactjs
Kendall B.
źródło
źródło
Odpowiedzi:
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.
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.
Rodzic z samotnym dzieckiem : używając wartości, którą przekazuje dziecku
JsFiddle
Rodzic z listą dzieci : nadal masz wszystko, czego potrzebujesz od rodzica i nie musisz komplikować dziecka.
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
null
kontekstem, 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:
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:
I uzyskaj dostęp do węzła DOM w obiekcie nadrzędnym za pomocą
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 ...:
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(...)
lubchildComponent.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:
Rodzic może być klasą (i może ostatecznie zarządzać samym stanem, ale przekazuję go tutaj jako rekwizyty:
Ale można to również uprościć, jeśli nie trzeba zarządzać stanem:
JsFiddle
OSTRZEŻENIE PERF (dotyczy ES5 / ES6): jeśli używasz
PureComponent
lubshouldComponentUpdate
, 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 zthis
konstruktorem klasy), abyPureComponent
można było rozpocząć optymalizację lub może zaimplementować własneshouldComponentUpdate
i 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:
W takim przypadku możesz zoptymalizować komponent potomny, używając:
źródło
props
. Zgodzić się?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ćbind
bezpośrednio w rodzicu.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. Nawiasemclass model
mówiąc, używam ES6 , więc zmieniłemnull
nathis
i mój kod nadal działa. Jak przetłumaczysz tę część kodu na styl ES6?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
onClick
byćthis.props.onClick.bind(null, this)
:Moduł obsługi zdarzeń w rodzicu może następnie uzyskać dostęp do komponentu i zdarzenia w następujący sposób:
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:
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
props
i stosuje je do komponentu. Dziecko wyglądałoby tak:Umożliwiając korzystanie z wartości bezpośrednio w obiekcie nadrzędnym:
Migawka JSBin
Po podłączeniu dodatkowych komponentów potomnych nie jest wymagana dodatkowa konfiguracja
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
ServiceItem
jest 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
handleClick
wywołania zwrotnego za pomocą podanegoindex
[ źródła ].Rodzice są dziećmi
DTServicesCalculator
jest elementem nadrzędnym w tym przykładzie. To także dziecko. Spójrzmy.DTServiceCalculator
tworzy listę elementów potomnychServiceItem
i dostarcza im rekwizyty [ źródło ]. Jest to komponent nadrzędny,ServiceItem
ale 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} />
handleServiceItem
przechwytuje indeks przekazany od dziecka i przekazuje go swojemu rodzicowi [ źródło ]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 ]:
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.
źródło
Parent
może to być widok kontrolera, podczas gdyChild
jest to widok głupi (komponent). Tylko widoki kontrolerów powinny mieć wiedzę o aplikacji. W takim przypadku nadal podajesz akcję odParent
doChild
jako 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/…onClick={this.onClick.bind(null, this.state.text)}
nie ma potrzeby wiązania stanu, ponieważ rodzic ma to. więc po prostuonClick={this.onClick}
zadziała. gdzieonClick = ()=>{const text = this.state.text;..}
w rodzicuWygląda na to, że istnieje prosta odpowiedź. Rozważ to:
Kluczem dzwoni
bind(null, this)
nathis.props.onClick
razie minęło od rodzica. Teraz funkcja onClick akceptuje argumentycomponent
ORAZevent
. 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.
źródło
this
z funkcją (która i tak nie jest potrzebna), a drugi jest wstawiany jako pierwszy argument, gdy wywoływany jest onClick.Pytanie brzmi, jak przekazać argument z elementu podrzędnego do elementu nadrzędnego. Ten przykład jest łatwy w użyciu i przetestowany:
Spójrz na JSFIDDLE
źródło
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
Header.js
Main.js
To wszystko, teraz możesz przekazywać wartości z klienta na serwer.
Spójrz na funkcję Zmień w Header.js
W ten sposób wypychasz wartości do rekwizytów od klienta do serwera
źródło
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)
Funkcja rodzica
Konstruktor macierzysty
Prop przekazany dziecku
Wezwanie na imprezy dla dzieci
źródło
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:
Objawem jest dziecko (w rekwizytach dziecka zadeklarowałem funkcję wywołania zwrotnego, w wybranej funkcji Objaw jest wywoływany wywołanie zwrotne):
źródło