користувацькі події React.js для спілкування з батьківськими вузлами


84

Я створюю і прослуховую звичайні DOM CustomEventдля зв'язку з батьківськими вузлами:

У дитини:

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

У батьків:

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

Це спрацьовує, але чи існує спосіб реалізації, який був би кращим?


6
Реагувати шлях буде проходити зворотні виклики вниз до дітей явно з допомогою реквізиту - <Child onCustomEvent={this.handleCustomEvent} />. У React немає підтримки користувацьких подій без бульбашок.
andreypopp

14
Отже, зменшити зворотні дзвінки, а не події вгору? Здається розумним.
forresto

22
@forresto люблю сарказм, +1
Dimitar Christoff

12
Я не був саркастичним.
forresto

5
одна справа встановити найкращу практику, а зовсім інша - запобігти життєздатності. такий різкий контраст, скажімо, twitter.github.io/flight - який використовує події DOME для роздуття та поширення синтетичних подій.
Dimitar Christoff

Відповіді:


47

Як зазначено вище:

Шляхом React було б передавати зворотні дзвінки дітям явно через реквізит -. У React немає підтримки користувацьких подій без бульбашок.

Абстракція реактивного програмування ортогональна:

Програмування інтерактивних систем за допомогою шаблону спостерігача є складним та схильним до помилок, але все ще є стандартом реалізації у багатьох виробничих середовищах. Ми представляємо підхід до поступової зневаги спостерігачів на користь реактивних абстракцій програмування. Кілька шарів бібліотеки допомагають програмістам плавно перенести існуючий код із зворотних викликів на більш декларативну модель програмування.

Філософія React базується на шаблоні Command:

введіть тут опис зображення

Список літератури


8
Мене цікавить, чому в React немає підтримки спеціальних синтетичних подій. Це просто те, що вони ніколи не були впроваджені, чи є дійсне дизайнерське рішення, чому їх не було ?. Чи вважаються події шкідливими, як висловлювання goto?
Michael Bylstra

4
@MichaelBylstra Користувальницькі події несхваленням з - за класичного питання : a class of debugging problems where you don't know what triggers some code because of a long chain of events triggering other events. Шаблон команди з потоком даних в одному напрямку , є React філософії.
Пол Світт,

2
Беручи до уваги той факт, що компоненти взаємодіють із сховищем даних, інжектованим предком за допомогою контексту React (Redux, React Router та ін. Всі використовують контекст), абсолютно брехливо стверджувати, що дитина передзвонює попереднику через будь-яку функцію в контексті. ідіоматичний React. Немає фактичної різниці між викликом Redux "відправлення" з об'єктом "action" та викликом "обробника подій" у контексті з об'єктом "event".
Енді,

1
Довгі ланцюги подій, що викликають інші події, досить поширені в тому, як деякі люди використовують Redux (наприклад, божевілля
Енді,

Я розумію думку, але у мене є питання. Припустимо, що можна було б побудувати бібліотеку UI-Kit, яка має на меті працювати безперебійно в різних архітектурах. Деякі компоненти можуть потребувати розсилки нестандартних подій, оскільки ми не можемо припустити нічого про невідому архітектуру. Суворе припущення, зроблене React, призводить до великої проблеми. Ви можете перевірити проблему тут ( custom-elements-everywhere.com ): React - це єдина бібліотека, яка не може обробляти належним чином власні події. Можливо, я щось пропускаю, і будь-яка пропозиція буде дуже вдячна.
7uc4,

7

Ви можете написати просту послугу, а потім скористатися нею

/** 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];
  },
};

приклад (те саме для спрацьовування)

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');
  }
}

4

Ви можете пускати події через зворотні дзвінки, передані через контекст: [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;

3

Є ще один, який я знайшов, який є також цілком розумним, особливо якщо свердління отворів від батьків до дитини вже стає громіздким. Він назвав це менш простим спілкуванням. Ось посилання:

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


Це, в основному, говорить "впровадити шаблон Observer". Я погоджуюсь з коментарем Майкла Билстри щодо ОП, який описує проблему, зазначену в "Менш простому спілкуванні", як "свердління отворів" ... без пухирців, передача зворотних викликів не обробляє глибинні структури> 1 рівня.
ericsoco

3

Можливе рішення, якщо вам абсолютно необхідно вдатися до шаблону Observer у програмі ReactJs, ви можете захопити звичайну подію. Наприклад, якщо ви хочете, щоб клавіша видалення викликала <div>те, що позначено для видалення, ви можете мати <div>прослуховування події зниження клавіш, яка буде викликана customEvent. Зафіксуйте клавіатуру на тілі та customEventнадішліть подію клавіатури на вибране <div>. Ділимось на випадок, якщо це комусь допоможе.


3

Центральний магазин [Redux], який роздає стан клієнтам, а потім стан відправлення назад до магазину також нагадує шаблон спостерігача. Спосіб зробити публікацію / передплату лише гірший через явні (крихкі?) Накладні витрати на підключення реквізиту / подій. Для злому через ієрархію React забезпечує контекст (шаблон постачальника) або спостережувані бібліотеки, які смердять. Як MobX, що представляє нові декоратори @observable, або Vue, що вводить новий синтаксис шаблону "v-if". Події і так є основним способом роботи DOM і циклу подій javascript, так чому б і ні? Думаю, це зробили сатаністи. Лол


1

Я усвідомлюю, що це питання на сьогодні досить давнє, але ця відповідь все одно може комусь допомогти. Я написав прагму JSX для React, яка додає декларативну власну подію: jsx-native-events .

В основному ви просто використовуєте onEvent<EventName>шаблон, щоб спостерігати за подіями.

<some-custom-element onEventSomeEvent={ callback }></some-custom-element>
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.