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?

 258
Author: Dave Newton, 2014-01-22

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:

  1. <Filters /> jest elementem potomnym <List />
  2. zarówno <Filters /> jak i <List /> są dziećmi komponentu nadrzędnego
  3. <Filters /> i <List /> żyją całkowicie w oddzielnych komponentach root.
Mogą być inne scenariusze, o których nie myślę. Jeśli twój nie mieści się w tym, daj mi znać. Oto kilka bardzo szorstkich przykładów jak radziłem sobie z dwoma pierwszymi scenariuszami:

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ą.

JSFiddle dla #1 →

/** @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 />.

JSFiddle dla #2 →

/** @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ń .

 287
Author: Michael LaCroix,
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>

Więcej szczegółów tutaj


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!

 126
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
2018-09-17 08:17:58

Tak to załatwiłem.
Załóżmy, że masz dla dzień. Liczba dni zależy od wybranego miesiąca.

Obie listy są własnością trzeciego obiektu, lewego panelu. Oba

 5
Author: Skulas,
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
 4
Author: Kaloyan Kosev,
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

Reaguj.js i EventSystem kod

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() {

    }
}
 2
Author: tsuz,
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 ...

Redux i Flux

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.

 1
Author: Alireza,
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 tam
class 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.
 0
Author: Shiroo,
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 .

 0
Author: Kemal Dağ,
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'));
 0
Author: cabolanoz,
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>
        );
    }
}
 0
Author: Sergei Zinovyev,
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.

 0
Author: windmaomao,
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