【问题标题】:React: AppStore listener must be a functionReact:AppStore 监听器必须是一个函数
【发布时间】:2015-11-23 14:38:41
【问题描述】:

各位开发者!

感谢 Egghead 的教程,我一直在深入研究 Flux / React。虽然我听说 React 正在移动,但我在遵循上述教程时遇到了 React 库中的一些变化。

到目前为止,我已经能够解决所有问题。现在,我遇到了关于商店的砖墙。正如教程所述,我正在制作一个购物车,用户可以在其中将东西添加到他们的购物车中。没那么复杂。实际添加有效,但未触发重新渲染。下面的代码给出了添加的错误(下面也会列出完整的代码):

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

这会导致 Uncaught TypeError: listener must be a function。回调参数未定义(这就是问题所在)。然而,因为我不仅仅是新的 React 的工作方式,我在定位问题时遇到了麻烦。以下 sn-p 是教程中的 Store:

var AppStore = assign(EventEmitter.prototype, {

    emitChange: function() {
        this.emit(CHANGE_EVENT);
    },
    addChangeListener: function(callback) {
        this.on(CHANGE_EVENT, callback);
    },
    removeChangeListener: function(callback) {
        this.removeChangeListener(CHANGE_EVENT, callback);
    },
    getCart: function() {
        return _cartItems;
    },
    getCatalog: function() {
        return _catalog;
    },
    getCartTotals: function() {
        return _cartTotals();
    },
    dispatcherIndex: AppDispatcher.register(function (payload) {
        var action = payload.action;

        switch(action.actionType) {
            case AppConstants.ADD_ITEM:
                _addItem(payload.action.item);
                break;
            case AppConstants.REMOVE_ITEM:
                _removeItem(payload.action.index);
                break;
            case AppConstants.INCREASE_ITEM:
                _increaseItem(payload.action.index);
                break;
            case AppConstants.DECREASE_ITEM:
                _decreaseItem(payload.action.index);
                break;
        }

        AppStore.emitChange();

        return true;
    })
});

请注意,这是来自 Egghead.io 教程的代码,我绝不是所有者(如果他们愿意,我将删除所述代码)。

如果需要更多代码或解释,我很乐意提供帮助!

谢谢大家:)

编辑 1:应该监听更改但没有更改的组件:

var Cart = React.createClass({

    getInitialState: function() {
        return cartItems();
    },
    componentWillMount: function() {
        debugger;
        AppStore.addChangeListener(this.onChange);
    },
    componentDidMount: function() {
        debugger;
        AppStore.addChangeListener(this.handleChange);
    },
    handleChange: function() {
        debugger;
        this.forceUpdate();
    },
    _onChange: function() {
        debugger;
        this.setState(cartItems());
    },
    render: function() {
        var total = 0;
        var items = this.state.items.map(function (item, i) {
            var subtotal = item.cost * item.qty;
            total +=subtotal;
            return (
                <tr key={i}>
                    <td><RemoveFromCart index={i} /></td>
                    <td>{item.title}</td>
                    <td>{item.qty}</td>
                    <td>
                        <Increase index={i} />
                        <Decrease index={i} />
                    </td>
                    <td>${subtotal}</td>
                </tr>
            );
        });

        return (
            <table className="table table-hover">
                <thead>
                    <tr>
                        <th></th>
                        <th>Item</th>
                        <th>Qty</th>
                        <th></th>
                        <th>Subtotal</th>
                    </tr>
                </thead>
                <tbody>
                    {items}
                </tbody>
                <tfoot>
                    <tr>
                        <td colSpan="4" className="text-right">Total</td>
                        <td>${total}</td>
                    </tr>
                </tfoot>
            </table>
        );
    }
});

【问题讨论】:

  • 您能否提供一个小的、可行的问题示例?此外,您是否尝试过调试代码以查看为什么回调为空?你从哪里调用“addChangeListener”?我假设一个组件?
  • 如果可以的话,我会的,但我们谈论的是一些 20'ish 文件和实际代码的版权问题(因为我不完全确定复制/分发的界限在哪里所述代码的谎言)。基本上,调用_addItem(payload.action.item) 函数会触发Appstore.emitChange() 行。这反过来会触发emitChange: function() 行,这会触发addChangeListener: function(callback) 行。自然,回调是未定义的。但是我对 React 的了解还不够,无法纠正这个:/
  • 为什么/如何emitChange 调用addChangeListener?它不应该。事件发出时调用哪个函数?好像你没有正确连接你的代码。顺便说一句,这与 React 无关。如果我们不知道如何调用 addChangeListener,我们将无法真正帮助您。
  • 你是对的 Felix Kling——我错了。请参阅我对 christopher 的第一条评论。

标签: javascript reactjs


【解决方案1】:

对我来说,听起来你已经解决了大部分这种模式,但你有点困惑。您需要包括最后一步,这实际上是将您的组件订阅到商店推出的事件。

this.emit(CHANGE_EVENT);

这只会推出一些东西。如果没有人在听,那么什么都不会改变,所以在你的组件中,你需要实际监听这个事件。

对于一个组件,可能类似于:

React.createClass({
    componentDidMount: function() {
        // Called after react has rendered the HTML in the DOM.
        AppStore.addChangeListener(this.handleChange);
    },

    render: function() {
        return <div>{AppStore.someData}</div>
    },

    handleChange: function() {
        this.forceUpdate();
    }
});

注意this.emit 不应调用 addChangeListener。它应该使用addChangeListener 方法触发您订阅的回调。

可能的改进

  • 需要注意的一点是,尽管您的所有数据都在AppStore 中,但并非所有组件都会关心所有数据。例如,如果你有一个Name 组件,它呈现用户名,他们只关心NAME_CHANGE_EVENT。他们不想每次发生小事时都重新渲染。这是 React 不必要的处理,尽管 React 会推测没有任何变化并且不会更新 DOM。

【讨论】:

  • 感谢您的建议!仍在到处调试以更好地了解此模式的内部:) 我犯了一个错误,说this.emit 触发addChangeListener,这不是真的。但是,我确实在应该更新的组件中放置了一个侦听器,但是当更新发生时,实际上并没有触发任何函数。我将在 OP 中发布组件。
  • 你在哪里定义了_addItem
  • 好吧,这个错误是我自己的愚蠢造成的。我忘了在this.onChange后面加一个_,应该是this._onChange。即使删除了您添加的功能,它仍然像魅力一样工作(尽管我正在调试它以找出原因*)。不过谢谢-我将您的答案作为解决方案投票,因为您的潜在改进很有帮助,它使我朝着正确的方向前进。 _addItem() 发生在 AppStore 的私有函数中(它是全局范围的,在编译期间它被包装在自己的范围内)。再次感谢!
  • 有趣的是,这个问题得到了很大的关注。一定是常见问题。
  • 我使用的教程有点老了,我不得不自己修复一些其他的东西,起初我以为只是其他人遇到了同样的问题。或者,React 是一个热门项目。无论如何,再次感谢您的建议 :)
猜你喜欢
  • 1970-01-01
  • 2013-04-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-07
  • 2017-02-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多