Istnieje wiele sposobów komunikacji między komponentami. Niektóre mogą być dostosowane do twojej skrzynki użytkownika. Oto lista niektórych, które uznałem za przydatne.
Reagować
Bezpośrednia komunikacja rodzic / dziecko
const Child = ({fromChildToParentCallback}) => (
<div onClick={() => fromChildToParentCallback(42)}>
Click me
</div>
);
class Parent extends React.Component {
receiveChildValue = (value) => {
console.log("Parent received value from child: " + value); // value is 42
};
render() {
return (
<Child fromChildToParentCallback={this.receiveChildValue}/>
)
}
}
W tym przypadku komponent potomny wywoła wywołanie zwrotne dostarczone przez rodzica z wartością, a rodzic będzie mógł uzyskać wartość dostarczoną przez dzieci w rodzicu.
Jeśli tworzysz funkcję / stronę swojej aplikacji, lepiej jest mieć samotnego rodzica zarządzającego wywołaniami zwrotnymi / stanem (zwanym także container
lub smart component
), a wszystkie dzieci muszą być bezpaństwowcami. W ten sposób możesz łatwo „udostępnić” stan rodzica każdemu dziecku, które tego potrzebuje.
Kontekst
Kontekst React pozwala na zachowanie stanu w katalogu głównym hierarchii komponentów i umożliwia łatwe wstrzykiwanie tego stanu do bardzo głęboko zagnieżdżonych komponentów, bez konieczności przekazywania rekwizytów do wszystkich komponentów pośrednich.
Do tej pory kontekst był funkcją eksperymentalną, ale nowy interfejs API jest dostępny w React 16.3.
const AppContext = React.createContext(null)
class App extends React.Component {
render() {
return (
<AppContext.Provider value={{language: "en",userId: 42}}>
<div>
...
<SomeDeeplyNestedComponent/>
...
</div>
</AppContext.Provider>
)
}
};
const SomeDeeplyNestedComponent = () => (
<AppContext.Consumer>
{({language}) => <div>App language is currently {language}</div>}
</AppContext.Consumer>
);
Konsument używa wzorca renderowania / funkcji potomnych
Sprawdź ten post na blogu, aby uzyskać więcej informacji.
Przed React 16.3, zalecałbym używanie transmisji telewizyjnej, która oferuje dość podobny interfejs API i używanie wcześniejszego kontekstowego interfejsu API.
Portale
Skorzystaj z portalu, jeśli chcesz trzymać dwa komponenty blisko siebie, aby komunikować się za pomocą prostych funkcji, takich jak w normalnym obiekcie nadrzędnym / podrzędnym, ale nie chcesz, aby te 2 składniki miały relację nadrzędny / podrzędny w DOM, ponieważ implikowanych ograniczeń wizualnych / CSS (takich jak indeks Z, krycie ...).
W takim przypadku możesz użyć „portalu”. Istnieją różne biblioteki reagujące za pomocą portali , zwykle używanych do modałów , wyskakujących okienek, podpowiedzi ...
Rozważ następujące:
<div className="a">
a content
<Portal target="body">
<div className="b">
b content
</div>
</Portal>
</div>
Po wygenerowaniu może wygenerować następujący DOM reactAppContainer
:
<body>
<div id="reactAppContainer">
<div className="a">
a content
</div>
</div>
<div className="b">
b content
</div>
</body>
Więcej informacji tutaj
Automaty
Definiujesz gdzieś szczelinę, a następnie wypełniasz ją z innego miejsca drzewa renderowania.
import { Slot, Fill } from 'react-slot-fill';
const Toolbar = (props) =>
<div>
<Slot name="ToolbarContent" />
</div>
export default Toolbar;
export const FillToolbar = ({children}) =>
<Fill name="ToolbarContent">
{children}
</Fill>
Jest to trochę podobne do portali, z tym że wypełniona zawartość będzie renderowana w zdefiniowanym gnieździe, podczas gdy portale generalnie renderują nowy węzeł dom (często dzieci dokumentu.body)
Sprawdź bibliotekę reaguj-zapełnij szczelinę
Autobus zdarzeń
Jak stwierdzono w dokumentacji React :
Do komunikacji między dwoma komponentami, które nie mają relacji rodzic-dziecko, możesz skonfigurować własny globalny system zdarzeń. Subskrybuj zdarzenia w module componentDidMount (), anuluj subskrypcję w module componentWillUnmount () i wywołaj setState () po odebraniu zdarzenia.
Istnieje wiele rzeczy, których możesz użyć do skonfigurowania magistrali zdarzeń. Możesz po prostu utworzyć tablicę detektorów, a po opublikowaniu zdarzenia wszyscy detektory otrzymaliby zdarzenie. Lub możesz użyć czegoś takiego jak EventEmitter lub PostalJs
Strumień
Strumień jest zasadniczo magistralą zdarzeń, z tym wyjątkiem, że odbiorniki zdarzeń są sklepami. Jest to podobne do podstawowego systemu magistrali zdarzeń, z tym wyjątkiem, że stan jest zarządzany poza React
Oryginalna implementacja Fluxa wygląda jak próba hackowania Event-sourcingu.
Redux jest dla mnie implementacją Flux, która jest najbliższa pozyskiwaniu zdarzeń, ma wiele zalet związanych z pozyskiwaniem zdarzeń, takich jak możliwość podróżowania w czasie. Nie jest ściśle powiązany z React i może być również używany z innymi bibliotekami widoków funkcjonalnych.
Samouczek wideo Redhead firmy Egghead jest naprawdę fajny i wyjaśnia, jak działa wewnętrznie (jest naprawdę prosty).
Kursory
Kursory pochodzą z ClojureScript / Om i są szeroko stosowane w projektach React. Pozwalają zarządzać stanem poza React i pozwalają wielu komponentom na dostęp do odczytu / zapisu do tej samej części stanu, bez konieczności posiadania wiedzy na temat drzewa komponentów.
Istnieje wiele implementacji, w tym ImmutableJS , React-cursors i Omniscient
Edycja 2016 : wydaje się, że ludzie zgadzają się, że kursory działają dobrze w przypadku mniejszych aplikacji, ale nie skaluje się dobrze w złożonych aplikacjach. Om Next nie ma już kursorów (chociaż to Om początkowo wprowadził tę koncepcję)
Architektura wiązu
Architektura Elm to architektura zaproponowała być wykorzystywane przez języku Elm . Nawet jeśli Elm nie jest ReactJS, architektura Elm może być również wykonana w React.
Dan Abramov, autor Redux, wykonał implementację architektury Elm przy użyciu React.
Zarówno Redux, jak i Elm są naprawdę świetne i mają tendencję do wzmacniania koncepcji pozyskiwania zdarzeń na froncie, umożliwiając debugowanie w czasie, cofanie / ponawianie, odtwarzanie ...
Główna różnica między Redux a Elm polega na tym, że Elm jest o wiele bardziej rygorystyczny w zarządzaniu stanem. W Elm nie możesz mieć stanu lokalnego komponentu ani haków montowania / odmontowywania, a wszystkie zmiany DOM muszą być wywoływane przez globalne zmiany stanu. Architektura wiązów proponuje skalowalne podejście, które pozwala obsłużyć WSZYSTKIE stany wewnątrz jednego niezmiennego obiektu, podczas gdy Redux proponuje podejście, które zaprasza do obsługi WIĘKSZOŚCI stanu w pojedynczym niezmiennym obiekcie.
Podczas gdy koncepcyjny model Elm jest bardzo elegancki, a architektura pozwala dobrze skalować się w dużych aplikacjach, w praktyce może być trudny lub wymagać większej liczby elementów do wykonania prostych zadań, takich jak skupienie się na danych wejściowych po zamontowaniu lub integracja z istniejącą biblioteką z interfejsem imperatywnym (tj. wtyczką JQuery). Powiązany problem .
Również architektura Elm wymaga większej liczby kodów. Pisanie nie jest takie pełne i skomplikowane, ale myślę, że architektura Elm jest bardziej odpowiednia dla języków o typie statycznym.
FRP
Biblioteki takie jak RxJS, BaconJS lub Kefir mogą być używane do tworzenia strumieni FRP do obsługi komunikacji między komponentami.
Możesz spróbować na przykład Rx-React
Myślę, że używanie tych bibliotek jest dość podobne do używania tego, co oferuje język ELM z sygnałami .
Struktura CycleJS nie używa ReactJS, ale używa vdom . Ma wiele podobieństw z architekturą Elm (ale jest łatwiejszy w użyciu, ponieważ pozwala na haczyki vdom) i używa RxJs intensywnie zamiast funkcji i może być dobrym źródłem inspiracji, jeśli chcesz używać FRP z Reagować. Filmy z CycleJs Egghead są miłe, aby zrozumieć, jak to działa.
CSP
CSP (Communicating Sequential Processes) są obecnie popularne (głównie ze względu na Go / goroutines i core.async / ClojureScript), ale można ich używać również w javascript z JS-CSP .
James Long nakręcił film wyjaśniający, jak można go używać z React.
Sagi
Saga to koncepcja zaplecza, która pochodzi ze świata DDD / EventSourcing / CQRS, zwana także „menedżerem procesów”. Jest popularyzowany przez projekt redux-saga , głównie jako zamiennik redux-thunk do obsługi efektów ubocznych (tj. Wywołań API itp.). Większość ludzi uważa obecnie, że służy to jedynie do działań niepożądanych, ale tak naprawdę chodzi raczej o oddzielenie komponentów.
Jest to raczej komplement dla architektury Flux (lub Redux) niż całkowicie nowy system komunikacji, ponieważ saga emituje akcje Flux na końcu. Chodzi o to, że jeśli masz widget1 i widget2 i chcesz, aby były one oddzielone, nie możesz strzelać do akcji widget2 z widget1. Dzięki temu widżet1 wykonuje tylko te akcje strzelania, które są skierowane na siebie, a saga jest „procesem w tle”, który nasłuchuje akcji widget1 i może wywoływać akcje skierowane na widget2. Saga jest punktem łączącym 2 widżety, ale widżety pozostają oddzielone.
Jeśli jesteś zainteresowany, spójrz na moją odpowiedź tutaj
Wniosek
Jeśli chcesz zobaczyć przykład tej samej małej aplikacji używającej tych różnych stylów, sprawdź gałęzie tego repozytorium .
Nie wiem, która opcja jest najlepsza na dłuższą metę, ale bardzo podoba mi się to, jak Flux wygląda jak pozyskiwanie zdarzeń.
Jeśli nie znasz koncepcji pozyskiwania zdarzeń , zapoznaj się z tym bardzo pedagogicznym blogiem: Przekształcanie bazy danych na lewą stronę za pomocą apache Samza , należy przeczytać, aby zrozumieć, dlaczego Flux jest fajny (ale może to również dotyczyć FRP )
Myślę, że społeczność zgadza się, że najbardziej obiecującą implementacją Fluxa jest Redux , który stopniowo zapewni bardzo produktywne doświadczenie programistyczne dzięki gorącemu przeładowaniu. Imponujące kodowanie na żywo ala Bret Victor's Inventing on Principle jest możliwe!