ReactJS dwa komponenty komunikujące się
Właśnie zacząłem z ReactJS i trochę utknąłem na problem, który mam.
Moja aplikacja jest zasadniczo listą z filtrami i przyciskiem do zmiany układu.
W tej chwili używam trzech komponentów: <list />
, < Filters />
i <TopBar />
, teraz oczywiście, gdy zmienię ustawienia w < Filters />
chcę uruchomić jakąś metodę w <list />
, aby zaktualizować mój widok.
Jak mogę sprawić, by te 3 komponenty współdziałały ze sobą, czy potrzebuję jakiegoś globalnego modelu danych, w którym mogę po prostu zrobić zmiany w?
11 answers
Najlepsze podejście zależy od tego, jak planujesz zorganizować te komponenty. Kilka przykładowych scenariuszy, które przychodzą teraz na myśl:
-
<Filters />
jest elementem potomnym<List />
- zarówno
<Filters />
jak i<List />
są dziećmi komponentu nadrzędnego -
<Filters />
i<List />
żyją całkowicie w oddzielnych komponentach root.
Scenariusz #1
Można przekazać funkcję obsługi z <List />
do <Filters />
, którą można następnie wywołać w zdarzeniu onChange
, aby filtrować listę z bieżącą wartością.
/** @jsx React.DOM */
var Filters = React.createClass({
handleFilterChange: function() {
var value = this.refs.filterInput.getDOMNode().value;
this.props.updateFilter(value);
},
render: function() {
return <input type="text" ref="filterInput" onChange={this.handleFilterChange} placeholder="Filter" />;
}
});
var List = React.createClass({
getInitialState: function() {
return {
listItems: ['Chicago', 'New York', 'Tokyo', 'London', 'San Francisco', 'Amsterdam', 'Hong Kong'],
nameFilter: ''
};
},
handleFilterUpdate: function(filterValue) {
this.setState({
nameFilter: filterValue
});
},
render: function() {
var displayedItems = this.state.listItems.filter(function(item) {
var match = item.toLowerCase().indexOf(this.state.nameFilter.toLowerCase());
return (match !== -1);
}.bind(this));
var content;
if (displayedItems.length > 0) {
var items = displayedItems.map(function(item) {
return <li>{item}</li>;
});
content = <ul>{items}</ul>
} else {
content = <p>No items matching this filter</p>;
}
return (
<div>
<Filters updateFilter={this.handleFilterUpdate} />
<h4>Results</h4>
{content}
</div>
);
}
});
React.renderComponent(<List />, document.body);
Scenariusz #2
Podobny do scenariusza # 1, ale komponent nadrzędny będzie przekazywał funkcję obsługi do <Filters />
, a filtrowaną listę do <List />
. Bardziej podoba mi się ta metoda ponieważ oddziela <List />
od <Filters />
.
/** @jsx React.DOM */
var Filters = React.createClass({
handleFilterChange: function() {
var value = this.refs.filterInput.getDOMNode().value;
this.props.updateFilter(value);
},
render: function() {
return <input type="text" ref="filterInput" onChange={this.handleFilterChange} placeholder="Filter" />;
}
});
var List = React.createClass({
render: function() {
var content;
if (this.props.items.length > 0) {
var items = this.props.items.map(function(item) {
return <li>{item}</li>;
});
content = <ul>{items}</ul>
} else {
content = <p>No items matching this filter</p>;
}
return (
<div className="results">
<h4>Results</h4>
{content}
</div>
);
}
});
var ListContainer = React.createClass({
getInitialState: function() {
return {
listItems: ['Chicago', 'New York', 'Tokyo', 'London', 'San Francisco', 'Amsterdam', 'Hong Kong'],
nameFilter: ''
};
},
handleFilterUpdate: function(filterValue) {
this.setState({
nameFilter: filterValue
});
},
render: function() {
var displayedItems = this.state.listItems.filter(function(item) {
var match = item.toLowerCase().indexOf(this.state.nameFilter.toLowerCase());
return (match !== -1);
}.bind(this));
return (
<div>
<Filters updateFilter={this.handleFilterUpdate} />
<List items={displayedItems} />
</div>
);
}
});
React.renderComponent(<ListContainer />, document.body);
Scenariusz # 3
Gdy komponenty nie mogą komunikować się między jakimkolwiek rodzajem relacji rodzic-dziecko, dokumentacja zaleca utworzenie globalnego systemu zdarzeń .
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
2014-01-24 00:54:29
Istnieje wiele sposobów komunikacji komponentów. Niektóre mogą być dostosowane do Twojego zastosowania. Oto lista niektórych, które uznałem za przydatne do poznania.
React
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}/>
)
}
}
Tutaj 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 mieć jednego rodzica zarządzanie wywołaniami zwrotnymi / stanem (nazywanymi również container
lub smart component
) i wszystkimi dziećmi, które mają być bezstanowe, tylko raportowanie rzeczy do rodzica. W ten sposób możesz łatwo "udostępnić" stan rodzica każdemu dziecku, które go potrzebuje.
Kontekst
React Context pozwala trzymać stan w katalogu głównym hierarchii komponentów i być w stanie łatwo wprowadzić ten stan do bardzo głęboko zagnieżdżonych komponentów, bez konieczności przekazywania rekwizytów każdemu pośrednikowi komponenty.
Do tej pory context był eksperymentalną funkcją, ale nowe API jest dostępne w Reaccie 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>
);
Użytkownik korzysta z wzorca funkcji render prop / children
Więcej informacji znajdziesz w tym wpisie na blogu.
Przed reactem 16.3, zalecałbym użycie react-broadcast, które oferują dość podobne API i używają dawnego API kontekstowego.
Portale
Użyj portalu, gdy chcesz zachować 2 komponenty Zamknij razem, aby komunikować się z prostymi funkcjami, jak w normalnym rodzic / dziecko, ale nie chcesz, aby te 2 komponenty miały relację rodzic/dziecko w DOM, ze względu na ograniczenia wizualne / CSS, które to implikuje (jak z-index, nieprzezroczystość...).
W tym przypadku możesz użyć "portalu". Istnieją różne biblioteki reactowe używające portali , zwykle używanych do modów , popupów, podpowiedzi...
Rozważ, co następuje:
<div className="a">
a content
<Portal target="body">
<div className="b">
b content
</div>
</Portal>
</div>
Może produkować po DOM po renderowaniu wewnątrz reactAppContainer
:
<body>
<div id="reactAppContainer">
<div className="a">
a content
</div>
</div>
<div className="b">
b content
</div>
</body>
Sloty
Definiujesz gdzieś szczelinę, a następnie wypełniasz ją z innego miejsca w drzewie 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 nieco podobne do portali, z tym że wypełniona zawartość będzie renderowana w zdefiniowanym przez Ciebie gnieździe, podczas gdy portale zazwyczaj renderują nowy węzeł dom (często potomek dokumentu.body)
Sprawdź react-slot-fill biblioteka
Zdarzenie autobus
Jak podano w dokumentacji Reacta :
Do komunikacji między dwoma komponentami, które nie mają relacji rodzic-dziecko, możesz skonfigurować własny globalny system zdarzeń. Subskrybuj zdarzenia za pomocą metody componentDidMount(), Anuluj subskrypcję za pomocą metody componentWillUnmount() i wywołaj setState () po otrzymaniu zdarzenia.
Jest wiele rzeczy, których możesz użyć do skonfigurowania szyny zdarzeń. Można po prostu utworzyć szereg słuchaczy, a na wydarzeniu publikować, wszystkie słuchacze otrzymaliby to wydarzenie. Możesz też użyć czegoś w rodzaju EventEmitter lub PostalJs
Flux
Flux jest w zasadzie magistralą zdarzeń, tyle że odbiornikami zdarzeń są magazyny. Jest to podobne do podstawowego systemu szyny zdarzeń, z tym że stan jest zarządzany poza React
Oryginalna implementacja Flux wygląda jak próba zrobienia Event-sourcingu w chwiejny sposób.
Redux jest dla mnie implementacją Flux, która jest najbliżej od event-sourcing, korzyści wiele korzyści event-sourcing jak zdolność do podróży w czasie. Nie jest ściśle powiązany z Reactem i może być również używany z innymi funkcjonalnymi bibliotekami widoków.
[8]}samouczek wideo Redux Eggheada jest naprawdę fajny i wyjaśnia, jak działa wewnętrznie (naprawdę jest prosty).Kursory
Kursory pochodzą z ClojureScript / Om i są szeroko stosowane w projektach Reactowych. Pozwalają na zarządzanie państwem poza Reacta i pozwolić wielu komponentom na dostęp do odczytu/zapisu do tej samej części stanu, bez potrzeby wiedzy o drzewie komponentów.
Istnieje wiele implementacji, w tym ImmutableJS, React-Kursory i Wszechwiedzący
Edit 2016: wydaje się, że ludzie zgadzają się, że Kursory działają dobrze dla mniejszych aplikacji, ale nie skalują się dobrze w złożonych aplikacjach. Om Next nie ma już kursorów (podczas gdy to Om wprowadził koncepcja początkowo)
Architektura Wiązów
Architektura Elm jest architekturą zaproponowaną do użycia przezjęzyk Elm . Nawet jeśli Elm nie jest ReactJS, Architektura Elm może być również wykonywana w Reaccie.
Dan Abramov, autor Redux, wykonałimplementację architektury Elm przy użyciu Reacta.
Zarówno Redux, jak i Elm są naprawdę świetne i mają tendencję do wzmacniania koncepcji event-sourcing na frontendzie, pozwalając debugowanie podróży w czasie, Cofnij/Ponów, powtórz...
Główna różnica między Redux i Elm jest to, że Elm wydają się być dużo bardziej rygorystyczne w zarządzaniu państwem. W Elm nie można mieć lokalnego stanu komponentu ani hooków montowania/odmontowywania, a wszystkie zmiany DOM muszą być wywołane globalnymi zmianami stanu. Architektura Elm proponuje skalowalne podejście, które pozwala obsłużyć wszystkie stan wewnątrz jednego niezmiennego obiektu, podczas gdy Redux proponuje podejście, które zachęca do obsługi większość stanu w jednym niezmiennym obiekcie.
Podczas gdy koncepcyjny model Elm jest bardzo elegancki, a architektura pozwala dobrze skalować duże aplikacje, może to w praktyce być trudne lub wymagać więcej boilerplate, aby osiągnąć proste zadania, takie jak nadanie ostrości do wejścia po zamontowaniu go, lub integracja z istniejącą biblioteką z imperatywnym interfejsem (IE jQuery plugin). zagadnienie powiązane .
Również architektura Elm wymaga większej ilości kodu. To nie jest tak gadatliwe lub skomplikowane do pisania, ale myślę, że architektura Elm jest bardziej nadaje się do języków statycznie typowanych.
FRP
Biblioteki takie jak RxJS, BaconJS lub Kefir mogą być używane do wytwarzania 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 język ELM oferuje zsygnałami .
Cyklejs framework do nie używaj ReactJS, ale używaj vdom. Ma wiele podobieństw do architektury Elm (ale jest łatwiejszy w użyciu w prawdziwym życiu, ponieważ pozwala na Hooki vdom) i używa szeroko RxJs zamiast funkcji i może być dobrym źródłem inspiracji, jeśli chcesz używać FRP z Reactem. filmiki 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 rdzeń.async/ClojureScript), ale można ich używać również w javascript z JS-CSP.
James Long zrobił wideo wyjaśniające, jak można go używać z Reactem.
Sagas
Saga to koncepcja zaplecza, która wywodzi się ze świata DDD / EventSourcing / CQRS, zwanego także "menedżerem procesów". Jest on spopularyzowany przez projekt redux-saga, głównie jako zamiennik redux-thunk do obsługi efektów ubocznych (np. wywołania API itp.). Większość ludzi obecnie myślę, że tylko Usługi dla skutków ubocznych, ale to jest rzeczywiście bardziej o odsprzęganie komponentów.Jest to bardziej komplement dla architektury Flux (lub Redux) niż całkowicie nowego systemu komunikacji, ponieważ Saga emituje działania Flux na końcu. Chodzi o to, że jeśli masz widget1 i widget2 i chcesz, aby były odsprzęgnięte, nie możesz odpalić action targeting widget2 z widget1. Więc widget1 tylko ogień działania, które celują się, a saga jest " tło process", który nasłuchuje akcji widget1 i może wysyłać akcje, które są adresowane do widget2. Saga jest punktem sprzęgającym między widgetami 2, ale widgety pozostają odsprzęgnięte.
Jeśli jesteś zainteresowany zajrzyj do moja odpowiedź tutaj
Podsumowanie
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 jaka jest najlepsza opcja na dłuższą metę, ale Bardzo podoba mi się, jak Flux wygląda na event-sourcing.
Jeśli nie znasz koncepcji event-sourcing, zajrzyj na ten bardzo Pedagogiczny blog: odwrócenie bazy danych na drugą stronę za pomocą apache Samza , jest to lektura obowiązkowa, aby zrozumieć, dlaczego Flux jest miły (ale może to dotyczyć również FRP)
Myślę, że społeczność zgadza się z tym, że najbardziej obiecującą implementacją Flux jest Redux , która dzięki hot reloading stopniowo pozwoli na bardzo produktywne doświadczenie deweloperów. Imponujące livecoding ala Bret Victor ' s wymyślanie na zasadzie wideo{[27] } jest możliwe!
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-09-17 08:17:58
Tak to załatwiłem.
Załóżmy, że masz
Obie listy są własnością trzeciego obiektu, lewego panelu. Oba Aby go przetestować, po prostu skopiuj kod do dwóch oddzielnych plików i uruchom indeks .html . Następnie wybierz miesiąc i zobacz, jak zmienia się liczba dni. Daty.js Oraz HTML do uruchomienia komponentu lewego panelu
indeks.html
Jest to gra z callbacków i obsługi w komponencie LeftPanel.
/** @jsx React.DOM */
var monthsLength = [0,31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
var MONTHS_ARR = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
var DayNumber = React.createClass({
render: function() {
return (
<option value={this.props.dayNum}>{this.props.dayNum}</option>
);
}
});
var DaysList = React.createClass({
getInitialState: function() {
return {numOfDays: 30};
},
handleMonthUpdate: function(newMonthix) {
this.state.numOfDays = monthsLength[newMonthix];
console.log("Setting days to " + monthsLength[newMonthix] + " month = " + newMonthix);
this.forceUpdate();
},
handleDaySelection: function(evt) {
this.props.dateHandler(evt.target.value);
},
componentDidMount: function() {
this.props.readyCallback(this.handleMonthUpdate)
},
render: function() {
var dayNodes = [];
for (i = 1; i <= this.state.numOfDays; i++) {
dayNodes = dayNodes.concat([<DayNumber dayNum={i} />]);
}
return (
<select id={this.props.id} onChange = {this.handleDaySelection}>
<option value="" disabled defaultValue>Day</option>
{dayNodes}
</select>
);
}
});
var Month = React.createClass({
render: function() {
return (
<option value={this.props.monthIx}>{this.props.month}</option>
);
}
});
var MonthsList = React.createClass({
handleUpdate: function(evt) {
console.log("Local handler:" + this.props.id + " VAL= " + evt.target.value);
this.props.dateHandler(evt.target.value);
return false;
},
render: function() {
var monthIx = 0;
var monthNodes = this.props.data.map(function (month) {
monthIx++;
return (
<Month month={month} monthIx={monthIx} />
);
});
return (
<select id = {this.props.id} onChange = {this.handleUpdate}>
<option value="" disabled defaultValue>Month</option>
{monthNodes}
</select>
);
}
});
var LeftPanel = React.createClass({
dayRefresh: function(newMonth) {
// Nothing - will be replaced
},
daysReady: function(refreshCallback) {
console.log("Regisering days list");
this.dayRefresh = refreshCallback;
},
handleMonthChange: function(monthIx) {
console.log("New month");
this.dayRefresh(monthIx);
},
handleDayChange: function(dayIx) {
console.log("New DAY: " + dayIx);
},
render: function() {
return(
<div id="orderDetails">
<DaysList id="dayPicker" dateHandler={this.handleDayChange} readyCallback = {this.daysReady} />
<MonthsList data={MONTHS_ARR} id="monthPicker" dateHandler={this.handleMonthChange} />
</div>
);
}
});
React.renderComponent(
<LeftPanel />,
document.getElementById('leftPanel')
);
<!DOCTYPE html>
<html>
<head>
<title>Dates</title>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js"></script>
<script src="//fb.me/react-0.11.1.js"></script>
<script src="//fb.me/JSXTransformer-0.11.1.js"></script>
</head>
<style>
#dayPicker {
position: relative;
top: 97px;
left: 20px;
width: 60px;
height: 17px;
}
#monthPicker {
position: relative;
top: 97px;
left: 22px;
width: 95px;
height: 17px;
}
select {
font-size: 11px;
}
</style>
<body>
<div id="leftPanel">
</div>
<script type="text/jsx" src="dates.js"></script>
</body>
</html>
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-07-22 15:26:05
Widziałem, że na pytanie jest już odpowiedź, ale jeśli chcesz dowiedzieć się więcej szczegółów, są w sumie 3 przypadki komunikacji między komponentami :
- Przypadek 1: Komunikacja rodzica z dzieckiem
- Przypadek 2: komunikacja dziecka z rodzicem
- Przypadek 3: niezwiązane komponenty (dowolny komponent do dowolnego komponentu) Komunikacja
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-24 16:24:18
Rozszerzenie odpowiedzi @ MichaelLaCroix gdy scenariusz jest taki, że komponenty nie mogą komunikować się między jakimkolwiek rodzajem relacji rodzic-dziecko, dokumentacja zaleca utworzenie globalnego systemu zdarzeń.
W przypadku <Filters />
i <TopBar />
nie mają żadnej z powyższych relacji, prosty globalny emiter może być użyty w następujący sposób:
componentDidMount
- Zapisz się do wydarzenia
componentWillUnmount
- Anuluj subskrypcję Wydarzenia
EventSystem.js
class EventSystem{
constructor() {
this.queue = {};
this.maxNamespaceSize = 50;
}
publish(/** namespace **/ /** arguments **/) {
if(arguments.length < 1) {
throw "Invalid namespace to publish";
}
var namespace = arguments[0];
var queue = this.queue[namespace];
if (typeof queue === 'undefined' || queue.length < 1) {
console.log('did not find queue for %s', namespace);
return false;
}
var valueArgs = Array.prototype.slice.call(arguments);
valueArgs.shift(); // remove namespace value from value args
queue.forEach(function(callback) {
callback.apply(null, valueArgs);
});
return true;
}
subscribe(/** namespace **/ /** callback **/) {
const namespace = arguments[0];
if(!namespace) throw "Invalid namespace";
const callback = arguments[arguments.length - 1];
if(typeof callback !== 'function') throw "Invalid callback method";
if (typeof this.queue[namespace] === 'undefined') {
this.queue[namespace] = [];
}
const queue = this.queue[namespace];
if(queue.length === this.maxNamespaceSize) {
console.warn('Shifting first element in queue: `%s` since it reached max namespace queue count : %d', namespace, this.maxNamespaceSize);
queue.shift();
}
// Check if this callback already exists for this namespace
for(var i = 0; i < queue.length; i++) {
if(queue[i] === callback) {
throw ("The exact same callback exists on this namespace: " + namespace);
}
}
this.queue[namespace].push(callback);
return [namespace, callback];
}
unsubscribe(/** array or topic, method **/) {
let namespace;
let callback;
if(arguments.length === 1) {
let arg = arguments[0];
if(!arg || !Array.isArray(arg)) throw "Unsubscribe argument must be an array";
namespace = arg[0];
callback = arg[1];
}
else if(arguments.length === 2) {
namespace = arguments[0];
callback = arguments[1];
}
if(!namespace || typeof callback !== 'function') throw "Namespace must exist or callback must be a function";
const queue = this.queue[namespace];
if(queue) {
for(var i = 0; i < queue.length; i++) {
if(queue[i] === callback) {
queue.splice(i, 1); // only unique callbacks can be pushed to same namespace queue
return;
}
}
}
}
setNamespaceSize(size) {
if(!this.isNumber(size)) throw "Queue size must be a number";
this.maxNamespaceSize = size;
return true;
}
isNumber(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
}
}
/ align = "left" / js
class NotificationComponent extends React.Component {
getInitialState() {
return {
// optional. see alternative below
subscriber: null
};
}
errorHandler() {
const topic = arguments[0];
const label = arguments[1];
console.log('Topic %s label %s', topic, label);
}
componentDidMount() {
var subscriber = EventSystem.subscribe('error.http', this.errorHandler);
this.state.subscriber = subscriber;
}
componentWillUnmount() {
EventSystem.unsubscribe('error.http', this.errorHandler);
// alternatively
// EventSystem.unsubscribe(this.state.subscriber);
}
render() {
}
}
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-08 06:33:21
OK, jest kilka sposobów, aby to zrobić, ale chcę skupić się wyłącznie na używaniu store używając Redux które znacznie ułatwiają Ci życie w tej sytuacji, zamiast dać Ci szybkie rozwiązanie tylko w tym przypadku, użycie czystego Reacta skończy się bałaganem w naprawdę dużej aplikacji i komunikacja między komponentami staje się coraz trudniejsza w miarę rozwoju aplikacji...
Więc co Redux robi dla Ciebie?
Redux jest jak magazyn lokalny w Twojej aplikacji które mogą być używane, gdy potrzebujesz danych, które mogą być używane w różnych miejscach aplikacji...
Zasadniczo idea Redux pochodzi z flux pierwotnie, ale z pewnymi fundamentalnymi zmianami, w tym koncepcją posiadania jednego źródła prawdy poprzez stworzenie tylko jednego sklepu...
Spójrz na poniższy wykres, aby zobaczyć pewne różnice między Flux i Redux ...
Rozważ zastosowanie Redux w swoim wniosku od początku, jeśli twój aplikacja wymaga komunikacji między komponentami...
Również czytanie tych słów z Dokumentacji Redux może być pomocne na początek:
Ponieważ wymagania dla aplikacji jednostronicowych JavaScript mają stają się coraz bardziej skomplikowane, nasz kod musi zarządzać więcej stanu niż ever before . Ten stan może obejmować odpowiedzi serwera i buforowane dane, jak również lokalnie utworzone dane, które nie zostały jeszcze zachowane do serwer. Zwiększa się również stan UI w złożoności, ponieważ musimy Zarządzaj aktywnymi trasami, wybranymi zakładkami, spinnerami, kontrolkami paginacji, i tak dalej.
Zarządzanie tym ciągle zmieniającym się stanem jest trudne. Jeśli model może zaktualizować inny model, a następnie Widok może zaktualizować model, który aktualizuje inny model, a to z kolei może spowodować aktualizację innego widoku. W niektórych punkt, nie rozumiesz już, co dzieje się w Twojej aplikacji, jak masz stracił kontrolę nad tym, kiedy, dlaczego i jak swojego stanu. Gdy system jest nieprzezroczysty i niedeterministycznych, trudno odtworzyć błędy lub dodać nowe funkcje.
Jakby to nie było wystarczająco złe, rozważ, że nowe wymagania stają się wspólne w rozwoju produktu front-end. Jako deweloperzy jesteśmy oczekuje obsługi aktualizacji, renderowania po stronie serwera, pobierania dane przed wykonaniem przejścia trasy i tak dalej. Znajdujemy siebie próba zarządzania złożonością, z którą nigdy nie mieliśmy do czynienia przed, i nieuchronnie zadajemy pytanie: czy nadszedł czas, aby dać w górę? Na odpowiedź brzmi nie.
Ta złożoność jest trudna do opanowania, ponieważ mieszamy dwa pojęcia które są bardzo trudne dla ludzkiego umysłu do rozumowania o: mutacji i asynchroniczność. Nazywam je Mentos i Cola. Oba mogą być świetne w separacja, ale razem tworzą bałagan. Biblioteki takie jak React spróbuj rozwiązać ten problem w warstwie widoku, usuwając oba asynchroniczna i bezpośrednia manipulacja DOM. Jednak zarządzanie stanem Twoje dane są pozostawione do ciebie. Tutaj Redux wchodzi.
Podążanie w krokach Flux, CQRS i Event Sourcing, Redux próby uczynienia mutacji państwowych przewidywalnymi, narzucając pewne ograniczenia dotyczące sposobu i czasu aktualizacji. Ograniczenia te znajdują odzwierciedlenie w trzech zasadach Redux.
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-08 10:03:53
Istnieje taka możliwość, nawet jeśli nie są to relacje rodzic - dziecko - i to jest Flux. Jest całkiem dobra (jak dla mnie osobiście) implementacja do tego o nazwie Alt.JS (z Alt-kontenerem).
Na przykład możesz mieć pasek boczny zależny od tego, co jest ustawione w szczegółach komponentu. Pasek boczny komponentu jest połączony z SidebarActions i SidebarStore, podczas gdy Details to DetailsActions i DetailsStore.
Możesz użyć AltContainer w ten sposób
<AltContainer stores={{
SidebarStore: SidebarStore
}}>
<Sidebar/>
</AltContainer>
{this.props.content}
Które będzie przechowywać sklepy (cóż mógłbym użyć" sklep "zamiast" sklepy " prop). Teraz, {to.rekwizyty.treść} mogą być Szczegóły w zależności od trasy. Powiedzmy, że /szczegóły przekierowują nas do tego widoku. Szczegóły będą miały na przykład pole wyboru, które zmieni element paska bocznego z X na Y, jeśli będzie zaznaczony.
Technicznie nie ma między nimi związku i trudno byłoby obejść się bez flux. Ale z tym jest to raczej łatwe.
Przejdźmy teraz do szczegółów. Stworzymy tamclass SiteActions {
constructor() {
this.generateActions(
'setSiteComponentStore'
);
}
setSiteComponent(value) {
this.dispatch({value: value});
}
}
I Detale
class SiteStore {
constructor() {
this.siteComponents = {
Prop: true
};
this.bindListeners({
setSiteComponent: SidebarActions.COMPONENT_STATUS_CHANGED
})
}
setSiteComponent(data) {
this.siteComponents.Prop = data.value;
}
}
A teraz, tutaj zaczyna się magia.
Jak widać nie jest bindListener do działań bocznych.ComponentStatusChanged, który zostanie użyty, jeśli zostanie użyty setSiteComponent.
Teraz w SidebarActions
componentStatusChanged(value){
this.dispatch({value: value});
}
Mamy coś takiego. Wyśle ten obiekt na wezwanie. I zostanie wywołana, jeśli zostanie użyty setSiteComponent in store (którego można użyć w komponencie na przykład podczas onChange on button ot whatever)
Teraz w SidebarStore będziemy mieli
constructor() {
this.structures = [];
this.bindListeners({
componentStatusChanged: SidebarActions.COMPONENT_STATUS_CHANGED
})
}
componentStatusChanged(data) {
this.waitFor(DetailsStore);
_.findWhere(this.structures[0].elem, {title: 'Example'}).enabled = data.value;
}
Teraz widać, że będzie czekać na szczegóły. Co to znaczy? mniej więcej oznacza to, że ta metoda musi czekać na Szczegółystore do aktualizacji, zanim będzie mogła sama się zaktualizować.
Tl;dr Jeden sklep nasłuchuje metod w sklepie i uruchomi akcję z akcji komponentu, która zaktualizuje własny sklep.
Mam nadzieję, że ci to pomoże.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-12-13 21:53:55
Jeśli chcesz zbadać możliwości komunikacji między komponentami i poczuć, że jest coraz trudniej, możesz rozważyć przyjęcie dobrego wzorca projektowego: Flux .
Jest to po prostu zbiór reguł, które określają, w jaki sposób przechowujesz i mutujesz stan aplikacji i używasz go do renderowania komponentów.
Istnieje wiele implementacji Flux, aOficjalna implementacja Facebook ' a jest jedną z nich. Chociaż uważa się, że ten, który zawiera większość kodu boilerplate, ale jest łatwiejszy do zrozumienia, ponieważ większość rzeczy są wyraźne.
Niektóre z innych alternatyw to flummox fluxxor fluxible i redux .
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-12-17 15:38:45
Kiedyś byłem tam, gdzie jesteś teraz, jako początkujący czasami czujesz się nie na miejscu, jak reagować sposób, aby to zrobić. Postaram się walczyć w ten sam sposób, w jaki teraz o tym myślę.
Państwa są podstawą komunikacji
Zazwyczaj sprowadza się to do sposobu, w jaki zmieniasz stany w tym komponencie w Twoim przypadku wskazujesz trzy komponenty.
<List />
: które prawdopodobnie wyświetli listę elementów w zależności od filtra
<Filters />
: opcje filtrowania, które będą zmień swoje dane.
<TopBar />
: lista opcji.
Aby zaaranżować całą tę interakcję, będziesz potrzebował wyższego komponentu, nazwijmy go aplikacją, która będzie przekazywać akcje i dane do każdego z tych komponentów, aby na przykład wyglądać tak]}
<div>
<List items={this.state.filteredItems}/>
<Filter filter={this.state.filter} setFilter={setFilter}/>
</div>
Zatem wywołanie setFilter
wpłynie na filteredItem i ponownie wyrenderuje oba komponenty;. W przypadku, gdy nie jest to do końca jasne, zrobiłem dla ciebie przykład z checkboxem, który możesz sprawdzić w pojedynczym pliku:
import React, {Component} from 'react';
import {render} from 'react-dom';
const Person = ({person, setForDelete}) => (
<div>
<input type="checkbox" name="person" checked={person.checked} onChange={setForDelete.bind(this, person)} />
{person.name}
</div>
);
class PeopleList extends Component {
render() {
return(
<div>
{this.props.people.map((person, i) => {
return <Person key={i} person={person} setForDelete={this.props.setForDelete} />;
})}
<div onClick={this.props.deleteRecords}>Delete Selected Records</div>
</div>
);
}
} // end class
class App extends React.Component {
constructor(props) {
super(props)
this.state = {people:[{id:1, name:'Cesar', checked:false},{id:2, name:'Jose', checked:false},{id:3, name:'Marbel', checked:false}]}
}
deleteRecords() {
const people = this.state.people.filter(p => !p.checked);
this.setState({people});
}
setForDelete(person) {
const checked = !person.checked;
const people = this.state.people.map((p)=>{
if(p.id === person.id)
return {name:person.name, checked};
return p;
});
this.setState({people});
}
render () {
return <PeopleList people={this.state.people} deleteRecords={this.deleteRecords.bind(this)} setForDelete={this.setForDelete.bind(this)}/>;
}
}
render(<App/>, document.getElementById('app'));
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-18 23:28:23
Poniższy kod pomaga mi skonfigurować komunikację między dwójką rodzeństwa. Konfiguracja odbywa się w ich rodzicach podczas wywołań render() i componentDidMount (). Opiera się na https://reactjs.org/docs/refs-and-the-dom.html Mam nadzieję, że to pomoże.
class App extends React.Component<IAppProps, IAppState> {
private _navigationPanel: NavigationPanel;
private _mapPanel: MapPanel;
constructor() {
super();
this.state = {};
}
// `componentDidMount()` is called by ReactJS after `render()`
componentDidMount() {
// Pass _mapPanel to _navigationPanel
// It will allow _navigationPanel to call _mapPanel directly
this._navigationPanel.setMapPanel(this._mapPanel);
}
render() {
return (
<div id="appDiv" style={divStyle}>
// `ref=` helps to get reference to a child during rendering
<NavigationPanel ref={(child) => { this._navigationPanel = child; }} />
<MapPanel ref={(child) => { this._mapPanel = child; }} />
</div>
);
}
}
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-18 14:00:29
O dziwo nikt nie wspomniał mobx
. Idea jest podobna do redux
. Jeśli mam kawałek danych, że wiele komponentów są subskrybowane do niego, to mogę użyć tych danych do napędu wielu komponentów.
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-09-07 17:35:12