首页 > 解决方案 > 在这个非常简单的虚拟应用程序上,错误的处理程序捕获了通量动作,为什么?

问题描述

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

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

大约是OrangesLemons,仅此而已。

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

下面是商店的代码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);

该代码几乎可以正常工作,但是...

我的问题是:对应的动作OrangesLemons监听器捕获,反之亦然。

如下图所示:

在此处输入图像描述

邀请您自己尝试:https ://codesandbox.io/s/24oq248v4n

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

在我看来,监听器Oranges应该只捕获Oranges动作,对于:Lemons

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

您对这种行为有任何想法吗?

如果可能,请在上面分叉我的代码并在此处提供您的固定代码的链接。

也欢迎一些解释。

谢谢!

标签: javascriptreactjsjsxfluxreactjs-flux

解决方案


回答我自己的问题:

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

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

Dispatcher 基本上是整个流程的管理者。它是您的应用程序的中心枢纽。分派器接收动作并将动作和数据分派给注册的回调。

所以它本质上是发布/订阅?

不完全是。调度程序将有效负载广播到其所有已注册的回调,并包含允许您以特定顺序调用回调的功能,甚至在继续之前等待更新。只有一个调度程序,它充当应用程序中的中心枢纽。

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

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

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

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

谢谢!


推荐阅读