在这个非常简单的虚拟应用程序上,错误的处理程序捕获了 Flux 操作,为什么?

Flux actions getting captured by wrong handlers on this very simple dummy app, Why?

我有这个非常简单的 React 应用程序:

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

大约 OrangesLemons,仅此而已。

它基本上从(模拟的)外部 API 获取 OrangesLemons,具体取决于按下的按钮。

下面是 Oranges 商店的代码(Lemons 的代码非常相似)。

src\resources\assets\js\stores\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 侦听器捕获,反之亦然。

如下图所示:

邀请您亲自试用:https://codesandbox.io/s/24oq248v4n

要查看结果:InfoErrors 您必须打开底部的 Code Sandbox 控制台。

在我看来,Oranges 的侦听器应该只捕获 Oranges 操作,同样适用于:Lemons.

我想我在这里省略了一个关键细节。

你知道这种行为吗?

如果可能,请 fork 我上面的代码,并在此处提供您的固定代码的 link。

也欢迎一些解释。

谢谢!

回答我自己的问题:

问题出在商店文件(两个)上,我有以下代码:

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

在这里您可以快速预览主要变化:

这里有一段有趣的内容:

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

The Dispatcher is basically the manager of this entire process. It is the central hub for your application. The dispatcher receives actions and dispatches the actions and data to registered callbacks.

So it's essentially pub/sub?

Not exactly. The dispatcher broadcasts the payload to ALL of its registered callbacks, and includes functionality that allows you to invoke the callbacks in a specific order, even waiting for updates before proceeding. There is only ever one dispatcher, and it acts as the central hub within your application.

此外,这里有两个 Flux 应用程序示例:

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

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

注意他们打电话的地方:.emitChange()

谢谢!