В этом очень простом фиктивном приложении действия Flux фиксируются неправильными обработчиками. Почему? - PullRequest
2 голосов
/ 07 марта 2019

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

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

, что примерно равно Oranges и Lemons, только это.

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

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

ЦСИ \ ресурсы \ активы \ JS \ магазины \ 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();

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

ЦСИ \ ресурсы \ активы \ JS \ действия \ 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();

Здесь у вас есть основной код:

ЦСИ \ 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.

Полагаю, здесь я пропускаю одну ключевую деталь.

Есть ли у вас какие-либо идеи об этом поведении?

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

Некоторое объяснение также приветствуется.

Спасибо!

1 Ответ

0 голосов
/ 07 марта 2019

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

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

src \ resources \ assets \ js \ stores \ 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 \ stores \ 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().

Спасибо!

...