Испускание и обработка глобальных событий с реагированием
Я немного поиграю, чтобы отреагировать на создание кнопки "Добавить в корзину". Вот мой код.
var ProductPurchase = React.createClass({
handleSubmit: function(e){
e.preventDefault();
$.ajax({
url: "/cart/add.js",
method: "post",
dataType: "json",
data: {
"id": this.props.variantId,
"quantity": this.props.quantity,
},
success: function(data) {
// emit cart added event
}.bind(this),
error: function(xhr, status, err) {
// emit error event (cart added)
}.bind(this)
});
},
getDefaultProps: function(){
return {
quantity: 1,
variantId: 231634908,
buttonText: "Add to cart"
}
},
render: function() {
return (
<div className="productPurchase">
<form action="/cart/add" method="post" enctype="multipart/form-data" onSubmit={this.handleSubmit}>
<input type="hidden" name="quantity" value={ this.props.quantity } />
<input type="hidden" name="id" value={ this.props.variantId } />
<button type="submit">{this.props.buttonText}</button>
</form>
</div>
);
}
});
Что мне интересно, так это обработчик ajax. Я почти уверен, что вся суть реакции - это взаимодействие между компонентами, за исключением того, что я не знаю, куда привести эти события. Я мог бы представить пару различных компонентов, таких как индикатор подсчета корзины, если успех или предупреждение об ошибке, если сбой, но я точно не знаю, как использовать их. Это целая точка диспетчеров потока?
Ответы
Ответ 1
Да, это определенно часть точки диспетчеров Flux - или любой эмитент событий, который вы хотели использовать.
Прежде чем вы спуститесь по этому пути, очень просто просто передать обработчики событий в качестве реквизитов без использования Flux или настраиваемых эмитентов событий - так же, как с обработчиками onSubmit
, onClick
и т.д. для обычных элементов DOM. Затем попросите родителя договориться с настройкой состояния и, возможно, передав его другим детям (через реквизиты).
Итак, в этом случае представьте родительский компонент, который имеет дело с событиями:
var RootComponent = React.createClass({
handleCartAdded: function(cart) {
console.log('Got a new cart: ' + cart);
}
handleError: function(err) {
console.error(err)
}
render: function() {
return (
<ProductPurchase onCartAdded={this.handleCartAdded} onError={this.handleError} />
)
}
})
И тогда соответствующая часть вашего компонента ProductPurchase
будет выглядеть следующим образом:
success: function(data) {
this.props.onCartAdded(data)
}.bind(this),
error: function(xhr, status, err) {
this.props.onError(err)
}.bind(this)
Более сложным примером может быть передача результата другому дочернему компоненту - но опять же, оставьте его родительским для управления этим:
var RootComponent = React.createClass({
handleCartAdded: function(cart) {
this.setState({cart: cart})
}
handleError: function(err) {
console.error(err)
}
render: function() {
return (
<div>
<ProductPurchase onCartAdded={this.handleCartAdded} onError={this.handleError} />
<CartSummary cart={this.state.cart} />
</div>
)
}
})
Таким образом, компоненты отделены друг от друга - и данные/функции могут быть переданы только четким контрактом (реквизитом).
Этот простой стиль обработки событий намного более явчен и легче отлаживается - поэтому я бы действительно использовал только архитектуру стиля Flux, если ваше приложение становится очень сложным и/или у вас есть много компонентов, которые все должны общаются друг с другом сложным образом.
Ответ 2
Flux используется для развязки программных конструкций (включая вызовы AJAX).
Вот диаграмма из Flux Docs
![Flux Architecture Diagram]()
Диспетчер в архитектуре Flux всегда остается в глобальной области. Поэтому любая операция, связанная с диспетчером, всегда встречается в глобальном масштабе. Кроме того, диспетчер и система событий имеют небольшую разницу, система событий всегда регистрирует обратные вызовы, привязанные к определенному событию, но в случае диспетчеров все обратные вызовы привязаны ко всем событиям.
Как использовать диспетчер? (Упрощенный подход без использования магазинов и ActionCreators)
-
Если на этот вызов AJAX влияет другая часть приложения, вам не следует делать этот вызов AJAX с этого компонента, скорее переместите вызов AJAX в новый файл и функцию. Например, используя CommonJS,
// CartApiUtils.js
module.exports = {
addToCart: function(item){
// AJAX call
}
}
-
Создайте AppDispatcher (который распространен во всем приложении) с помощью Flux Dispatcher class
var appDispatcher = new Dispatcher();
-
В функции addToCart() при успешном ответе AJAX отправьте событие с помощью AppDispatcher:
appDispatcher.dispatch({
actionType: 'cart.newItemAdded',
data: dataFromAjax
});
-
В вашем приложении везде, где вы хотите использовать это событие, вы можете просто зарегистрировать функцию для dispacher.
appDispatcher.register(function(payload){
if(payload.actionType === 'cart.newItemAdded'){
// payload.data contains the data
}
});
Это упрощенный подход. В более нормализованной структуре и более крупном приложении вы должны использовать Stores (что-то вроде уровня модели MVC, но не идентичного) и ActionCreator, где любое взаимодействие на уровне представления является действием пользователя и любым ответом вызова AJAX также становится действием с сервера.
Правило Thumb заключается в том, что Views должны быть заполнены (или обновлены) из магазинов, а магазины должны обновляться в событиях диспетчера.
Ответ 3
Как насчет использования npm "реакции-глобальные события"?
https://www.npmjs.com/package/react-global-events