Пользовательские события response.js для связи с родительскими узлами


85

Я создаю и слушаю обычные 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);
},

Это работает, но есть ли способ, специфичный для React, который был бы лучше?


7
Способ React заключается в явной передаче обратных вызовов дочерним элементам через props - <Child onCustomEvent={this.handleCustomEvent} />. В React нет поддержки пользовательских событий с всплыванием.
andreypopp

16
Итак, переместите обратные вызовы вниз вместо событий вверх? Кажется разумным.
forresto

23
@forresto люблю сарказм, +1
Димитар Кристофф

13
Я не был саркастичен.
forresto

5
одно дело - установить передовой опыт, другое - предотвратить жизнеспособный шаблон. такой разительный контраст, скажем, twitter.github.io/flight - который использует события DOME для создания пузырей и распространения синтетических событий.
Димитар Кристофф

Ответы:


50

Как указано выше:

Способ React заключается в явной передаче обратных вызовов дочерним элементам через props -. В React нет поддержки пользовательских событий с всплыванием.

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

Программирование интерактивных систем с помощью шаблона наблюдателя сложно и подвержено ошибкам, но по-прежнему является стандартом реализации во многих производственных средах. Мы представляем подход к постепенному отказу от наблюдателей в пользу абстракций реактивного программирования. Несколько библиотечных слоев помогают программистам плавно переносить существующий код с обратных вызовов на более декларативную модель программирования.

Вместо этого философия React основана на шаблоне Command:

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

Рекомендации


9
Меня интересует, почему в React нет поддержки пользовательских синтетических событий. Просто потому, что они никогда не были реализованы, или существует обоснованное дизайнерское решение, почему они не были реализованы? Считаются ли события вредными, как операторы goto?
Майкл Билстра,

5
@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 «dispatch» с объектом «действие» и вызовом «обработчика событий» в контексте с объектом «событие» нет.
Энди

2
Длинные цепочки событий, запускающие другие события, довольно распространены в том, как некоторые люди используют 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');
  }
}

5

Вы можете всплывать событиями через обратные вызовы, передаваемые через контексты: [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


По сути, это означает «реализовать шаблон наблюдателя». Я согласен с комментарием Майкла Билстры к OP, описывая проблему, отмеченную в «Менее простое общение», как «сверление дыр» ... без пузырей, передача обратных вызовов не обрабатывает структуры глубиной> 1 уровня.
ericoco

3

Возможное решение, если вы абсолютно необходимо прибегнуть к шаблону Observer в приложении ReactJs, вы можете захватить обычное событие. Например, если вы хотите, чтобы клавиша удаления вызывала событие, <div>помеченное для удаления, вы можете <div>прослушивать событие нажатия клавиши, которое будет вызываться customEvent. Захватите keydown на теле и customEventотправьте событие keydown для выбранного <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.