Zdarzenia niestandardowe wreak.js do komunikacji z węzłami nadrzędnymi

84

Tworzę i nasłuchuję normalnych DOM CustomEventdo komunikacji z węzłami nadrzędnymi:

U dziecka:

  var moveEvent = new CustomEvent('the-graph-group-move', { 
    detail: {
      nodes: this.props.nodes,
      x: deltaX,
      y: deltaY
    },
    bubbles: true
  });
  this.getDOMNode().dispatchEvent(moveEvent);

W rodzicu:

componentDidMount: function () {
  this.getDOMNode().addEventListener("the-graph-group-move", this.moveGroup);
},

To działa, ale czy istnieje sposób specyficzny dla React, który byłby lepszy?

forresto
źródło
6
Sposób React polegałby na jawnym przekazywaniu wywołań zwrotnych dzieciom za pośrednictwem rekwizytów - <Child onCustomEvent={this.handleCustomEvent} />. W Reakcie nie ma obsługi niestandardowych zdarzeń z bąbelkami.
andreypopp
14
Więc spowolnić wywołania zwrotne zamiast zwiększać liczbę zdarzeń? Wydaje się rozsądne.
forresto
22
@forresto uwielbiam sarkazm, +1
Dimitar Christoff
12
Nie byłam sarkastyczna.
forresto
5
wyznaczanie najlepszych praktyk to jedna rzecz, a zupełnie inna - zapobieganie realnym wzorcom. taki ostry kontrast, powiedzmy, twitter.github.io/flight - który wykorzystuje DOMEvents do bąbelkowania i propagowania syntetycznych wydarzeń.
Dimitar Christoff

Odpowiedzi:

47

Jak wspomniano wyżej:

Sposób React polegałby na jawnym przekazywaniu wywołań zwrotnych dzieciom za pośrednictwem rekwizytów -. W Reakcie nie ma obsługi niestandardowych zdarzeń z bąbelkami.

Abstrakcja programowania reaktywnego jest ortogonalna:

Programowanie systemów interaktywnych za pomocą wzorca obserwatora jest trudne i podatne na błędy, ale nadal jest standardem wdrożeniowym w wielu środowiskach produkcyjnych. Przedstawiamy podejście do stopniowego wycofywania obserwatorów na rzecz abstrakcji programowania reaktywnego. Kilka warstw biblioteki pomaga programistom w płynnej migracji istniejącego kodu z wywołań zwrotnych do bardziej deklaratywnego modelu programowania.

Zamiast tego, filozofia React opiera się na wzorcu Command:

wprowadź opis obrazu tutaj

Bibliografia

Paul Sweatte
źródło
8
Interesuje mnie, dlaczego w Reakcie nie ma obsługi niestandardowych zdarzeń syntetycznych. Czy po prostu nigdy nie zostały one wdrożone, czy też istnieje ważna decyzja projektowa, dlaczego tak się nie stało? Czy zdarzenia są uważane za szkodliwe, tak jak instrukcje goto?
Michael Bylstra
4
Zdarzenia @MichaelBylstra niestandardowe są mile widziana w związku z klasycznym wydaniu : a class of debugging problems where you don't know what triggers some code because of a long chain of events triggering other events. Wzór polecenia z jednokierunkowego przepływu danych są React filozofię.
Paul Sweatte
2
Biorąc pod uwagę fakt, że komponenty komunikują się z Datastore wstrzykniętym przez przodka za pośrednictwem kontekstu React (Redux, React Router itp. Wszystkie używają kontekstu), jest całkowicie fałszywe twierdzenie, że dziecko oddzwania do przodka przez jakąkolwiek funkcję w kontekście nie jest idiomatyczny React. Nie ma rzeczywistej różnicy między wywołaniem Redux „dispatch” z obiektem „action” a wywołaniem „obsługi zdarzenia” w kontekście z obiektem „zdarzenie”.
Andy
1
Długie łańcuchy wydarzeń wyzwalające inne wydarzenia są dość powszechne w sposobie, w jaki niektórzy ludzie używają Redux (np. Szaleństwo redux-saga)
Andy
Rozumiem, ale mam pytanie. Załóżmy, że można by zbudować bibliotekę UI-Kit, która ma działać bezproblemowo na wielu architekturach. Niektóre komponenty mogą wymagać wysyłania niestandardowych zdarzeń, ponieważ nie możemy założyć nic o nieznanej architekturze. Ścisłe założenie Reacta skutkuje dużym problemem. Możesz sprawdzić problem tutaj ( custom-elements-everywhere.com ): React to jedyna biblioteka, która nie może poprawnie obsługiwać niestandardowych zdarzeń. Może czegoś mi brakuje i każda sugestia byłaby bardzo mile widziana.
7uc4
7

możesz napisać prostą usługę, a następnie z niej skorzystać

/** eventsService */
module.exports = {
  callbacks: {},

  /**
   * @param {string} eventName
   * @param {*} data
   */
  triggerEvent(eventName, data = null) {
    if (this.callbacks[eventName]) {
      Object.keys(this.callbacks[eventName]).forEach((id) => {
        this.callbacks[eventName][id](data);
      });
    }
  },

  /**
   * @param {string} eventName name of event
   * @param {string} id callback identifier
   * @param {Function} callback
   */
  listenEvent(eventName, id, callback) {
    this.callbacks[eventName][id] = callback;
  },

  /**
   * @param {string} eventName name of event
   * @param {string} id callback identifier
   */
  unlistenEvent(eventName, id) {
    delete this.callbacks[eventName][id];
  },
};

przykład (to samo dotyczy wyzwalania)

import eventsService from '../../../../services/events';
export default class FooterMenu extends Component {
  componentWillMount() {
    eventsService
      .listenEvent('cart', 'footer', this.cartUpdatedListener.bind(this));
  }

  componentWillUnmount() {
    eventsService
      .unlistenEvent('cart', 'footer');
  }

  cartUpdatedListener() {
    console.log('cart updated');
  }
}
Ivan Maslov
źródło
4

Zdarzenia można wyświetlać poprzez wywołania zwrotne przekazywane w kontekstach: [CodePen]

import * as React from 'react';

const MyEventContext = React.createContext(() => {});

const MyEventBubbleContext = ({children, onMyEvent}) => {
  const bubbleEvent = React.useContext(MyEventContext);
  const handleMyEvent = React.useCallback((...args) => {
    // stop propagation if handler returns false
    if (onMyEvent(...args) !== false) {
      // bubble the event
      bubbleEvent(...args);
    }
  }, [onMyEvent]);
  return (
    <MyEventContext.Provider value={handleMyEvent}>
      {children}
    </MyEventContext.Provider>
  );
};

const MyComponent = () => (
  <MyEventBubbleContext onMyEvent={e => console.log('grandparent got event: ', e)}>
    <MyEventBubbleContext onMyEvent={e => console.log('parent got event: ', e)}>
      <MyEventContext.Consumer>
        {onMyEvent => <button onClick={onMyEvent}>Click me</button>}
      </MyEventContext.Consumer>
    </MyEventBubbleContext>
  </MyEventBubbleContext>
);

export default MyComponent;
Andy
źródło
3

Jest jeszcze jeden, który znalazłem, który jest całkiem rozsądny, zwłaszcza jeśli wiercenie otworów od rodzica do dziecka staje się już uciążliwe. Nazwał to mniej prostą komunikacją. Oto link:

https://github.com/ryanflorence/react-training/blob/gh-pages/lessons/04-less-simple-communication.md

indeks
źródło
Zasadniczo oznacza to „implementuj wzorzec obserwatora”. Zgadzam się z komentarzem Michaela Bylstry na temat PO, opisującym problem zauważony w Less Simple Communication jako „wiercenie otworów” ... bez bulgotania, przekazywanie wywołań zwrotnych nie obsługuje struktur o głębokości większej niż 1 poziom.
ericsoco
3

Możliwe rozwiązanie, jeśli absolutnie musisz uciekać się do wzorca Observer w aplikacji ReactJs, możesz przejąć normalne zdarzenie. Na przykład, jeśli chcesz, aby klawisz delete powodował <div>zaznaczenie do usunięcia, możesz <div>nasłuchiwać zdarzenia keydown, które zostanie wywołane przez zdarzenie customEvent. Uwięź keydown na ciele i customEventwywołaj zdarzenie keydown na wybranym <div>. Udostępnianie na wypadek, gdyby komuś pomogło.

Doug
źródło
3

Magazyn centralny [Redux], który rozprowadza stan do klientów, a następnie stan „wysyłania” z powrotem do sklepu jest również wzorem obserwatora. Sposób na publikowanie / subskrybowanie tylko gorzej z powodu jawnego (kruchego?) Narzutu łączącego ścieżki rekwizytów / wydarzeń. Aby zhakować hierarchię, React zapewnia kontekst (wzorzec dostawcy) lub obserwowalne biblioteki, które śmierdzą. Podobnie jak MobX, który wprowadza nowe dekoratory @observable lub Vue, który wprowadza nową składnię szablonów „v-if”. Zdarzenia są i tak głównym sposobem działania pętli zdarzeń DOM i javascript, więc dlaczego nie? Myślę, że zrobili to sataniści. Lol

user982671
źródło
1

Zdaję sobie sprawę, że to pytanie jest już dość stare, ale ta odpowiedź może komuś pomóc. Napisałem pragmę JSX dla Reacta, która dodaje deklaratywne zdarzenie niestandardowe: jsx-native-events .

Zasadniczo po prostu używasz onEvent<EventName>wzorca do obserwowania wydarzeń.

<some-custom-element onEventSomeEvent={ callback }></some-custom-element>
Caleb Williams
źródło