У меня есть очень простое приложение React:

https://codesandbox.io/s/24oq248v4n

Что касается Oranges и Lemons, именно этого.

Он в основном получает Oranges или Lemons из (смоделированного) внешнего API, в зависимости от нажатой кнопки.

Ниже у вас есть код для магазина Oranges (код для Lemons очень похож).

src \ resources \ assets \ js \ store \ OrangeStore.js

import { EventEmitter } from "events";
import { sprintf } from "sprintf-js";
import AppDispatcher from "../dispatcher/AppDispatcher";
import AppApi from "../utils/AppApi";

const CHANGE_EVENT = "change";
let _response = null;

class OrangeStore extends EventEmitter {
  constructor() {
    super();
    this.dispatchToken = AppDispatcher.register(this.handleActions.bind(this));
  }

  emitChange() {
    this.emit(CHANGE_EVENT);
  }

  addChangeListener(callback) {
    this.on(CHANGE_EVENT, callback);
  }

  removeChangeListener(callback) {
    this.removeListener(CHANGE_EVENT, callback);
  }

  fetchOranges(data) {
    AppApi.fetchOranges(data);
  }

  setOrangeResponse(data) {
    _response = data;
  }

  getOrangeResponse() {
    return _response;
  }

  clearOrangeResponse() {
    _response = null;
  }

  handleActions(action) {
    let nameObjectClass = this.constructor.name;
    switch (action.type) {
      case "FETCH_ORANGES":
        this.fetchOranges(action.value);
        break;
      case "SET_ORANGE_RESPONSE":
        this.setOrangeResponse(action.value);
        break;
      default:
        console.error(sprintf('ATTENTION: action: "%s" entered on the wrong handle, the one for: "%s".', action.type, nameObjectClass));
        break;
    }
    this.emitChange();
  }
}

export default new OrangeStore();

Вот код, который отвечает за отправку действий:

src \ resources \ assets \ js \ actions \ AppActions.js

import AppDispatcher from "../dispatcher/AppDispatcher";

class AppActions {
  fetchOranges(data) {
    AppDispatcher.dispatch({
      type: "FETCH_ORANGES",
      value: data
    });
  }
  setOrangeResponse(data) {
    AppDispatcher.dispatch({
      type: "SET_ORANGE_RESPONSE",
      value: data
    });
  }
  fetchLemons(data) {
    AppDispatcher.dispatch({
      type: "FETCH_LEMONS",
      value: data
    });
  }
  setLemonResponse(data) {
    AppDispatcher.dispatch({
      type: "SET_LEMON_RESPONSE",
      value: data
    });
  }
}

export default new AppActions();

Вот основной код:

src \ index.js

import React from "react";
import ReactDOM from "react-dom";
import AppActions from "./resources/assets/js/actions/AppActions";
import OrangeStore from "./resources/assets/js/stores/OrangeStore";
import LemonStore from "./resources/assets/js/stores/LemonStore";

import "./styles.css";

class App extends React.Component {

  client = {
    firstName: 'George',
    lastName: 'Washington',
  };

  componentWillMount() {
    OrangeStore.addChangeListener(this.handleOrangeResponse);
    LemonStore.addChangeListener(this.handleLemonResponse);
  }
  componentWillUnmount() {
    OrangeStore.removeChangeListener(this.handleOrangeResponse);
    LemonStore.removeChangeListener(this.handleLemonResponse);
  }

  getOranges = () => {
    AppActions.fetchOranges(this.client);
  };
  getLemons = () => {
    AppActions.fetchLemons(this.client);
  };

  handleOrangeResponse = () => {
    let response = OrangeStore.getOrangeResponse();
    console.log('inside: index.js / handleOrangeResponse() {...} | where: response == ', response);
  }
  handleLemonResponse = () => {
    let response = LemonStore.getLemonResponse();
    console.log('inside: index.js / handleLemonResponse() {...} | where: response == ', response);
  }

  render() {
    return (
      <div className="App">
        <h1>Oranges and Lemons</h1>
        <h2>Yet another Flux test!</h2>
        <button onClick={this.getOranges}>Get Oranges</button>
        <button onClick={this.getLemons}>Get Lemons</button>
      </div>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Код работает почти нормально, но ...

Моя проблема: действия, соответствующие Oranges, захватываются слушателем Lemons и наоборот.

Как вы можете видеть на следующем изображении:

enter image description here

Приглашаем вас попробовать это самостоятельно на: https://codesandbox.io/s/24oq248v4n

Чтобы увидеть результаты: Info и Errors вы должны открыть консоль Code Sandbox внизу.

На мой взгляд, слушатели для Oranges должны захватывать только действия Oranges и то же самое для: Lemons.

Думаю, я опускаю здесь одну ключевую деталь.

Вы хоть представляете такое поведение?

Если возможно, пожалуйста, разветвите мой код выше и предоставьте ссылку на свой фиксированный код здесь.

Также приветствуются некоторые пояснения.

Благодарность!

2
Viewsonic 7 Мар 2019 в 04:47

1 ответ

Лучший ответ

Отвечая на мой вопрос:

Проблема была в файлах магазина (обоих), где у меня был следующий код:

src \ resources \ assets \ js \ store \ OrangeStore.js (то же самое относится и к другому магазину)

class OrangeStore extends EventEmitter {

  ...

  handleActions(action) {
    let nameObjectClass = this.constructor.name;
    switch (action.type) {
      case "FETCH_ORANGES":
        this.fetchOranges(action.value);
        break;
      case "SET_ORANGE_RESPONSE":
        this.setOrangeResponse(action.value);
        break;
      default:
        console.error(sprintf('ATTENTION: action: "%s" entered on the wrong handle, the one for: "%s".', action.type, nameObjectClass));
        break;
    }
    this.emitChange();
  }

  ...

}

Здесь я допустил одну ошибку, и я вызвал ее: this.emitChange(); для всех видов действий, включая те, которые не связаны с соответствующим хранилищем.

Вот решение.

src \ resources \ assets \ js \ store \ OrangeStore.js (то же самое относится и к другому магазину)

class OrangeStore extends EventEmitter {

  ...

  handleActions(action) {
    switch (action.type) {
      case "FETCH_LEMONS":
        this.fetchLemons(action.value);
        // this.emitChange(); // THIS IS NOT NECESSARY HERE
        break;
      case "SET_LEMON_RESPONSE":
        this.setLemonResponse(action.value);
        this.emitChange();
        break;
    }
  }

  ...

}

Также обратите внимание, что нет необходимости вызывать: this.emitChange() для каждого действия, потому что если мы это сделаем, то механизм Flux будет вызывать ненужные обработчики событий (или обратные вызовы), и если у нас есть какие-то своего рода предварительная обработка этих обработчиков, которая всегда выполняется перед выполнением своих основных функций, тогда мы собираемся выполнить это в случае необходимости.

Когда мы имеем дело с несколькими магазинами, если мы не будем этого помнить, мы столкнемся с проблемами.

Вот исправленный код: https://codesandbox.io/s/0pml774y9l

Здесь у вас есть быстрый предварительный просмотр основных изменений:

enter image description here

Интересный абзац здесь:

https://scotch.io/tutorials/getting-to-know-flux-the-react-js-architecture

Диспетчер по сути является менеджером всего этого процесса. Это центральный узел для вашего приложения. Диспетчер получает действия и отправляет действия и данные зарегистрированным обратным вызовам.

Так это по сути паб / саб?

Не совсем. Диспетчер передает полезную нагрузку ВСЕМ своим зарегистрированным обратным вызовам и включает функциональные возможности, которые позволяют вам вызывать обратные вызовы в определенном порядке, даже ожидая обновлений, прежде чем продолжить. Есть только один диспетчер, и он действует как центральный узел в вашем приложении.

Кроме того, здесь есть два примера приложений Flux:

https://scotch.io/tutorials/build-a-react-flux-app-with-user-authentication

https://www.3pillarglobal.com/insights/getting-started-flux-react

Обратите внимание, где они звонят: .emitChange().

Благодарность!

1
Viewsonic 7 Мар 2019 в 16:54