我如何处理 React JS 中的 Stores 监听器?

How do I handle Store's listeneres in ReactJS?

这是平均商店:

// StoreUser
import EventEmitter from 'eventemitter3';
import Dispatcher from '../dispatcher/dispatcher';
import Constants from '../constants/constants';
import React from 'react/addons';
import serverAddress from '../utils/serverAddress';
import socket from './socket';

var CHANGE_EVENT = "change";
var storedData = {
  userName: null
}

socket
  .on('newUserName', (newUserName)=>{
    storedData.userName = newUserName;
    AppStore.emitChange();
  })


function _changeUserName (newUserName) {
  socket.emit('signUp', newUserName)
}

var AppStore = React.addons.update(EventEmitter.prototype, {$merge: {

  emitChange(){
    // console.log('emitChange')
    this.emit(CHANGE_EVENT);
  },

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

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

  getStoredData(callback) { 
    callback(storedData);
  },

  dispatcherIndex:Dispatcher.register(function(payload){
    var action = payload.action;
    switch(action.actionType){
      case Constants.CHANGE_USER_NAME:
        _changeUserName(payload.action.actionArgument);
        break;
    }
    return true;
  })
}});

export default AppStore;

这是连接到商店的平均组件:

import React from 'react';

import StoreUser from '../../stores/StoreUser';

import Actions from '../../actions/Actions';

export default class User extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      userName: null,
    };
  }

  componentDidMount() {
    StoreUser.addChangeListener(this._onChange.bind(this));
  }

  componentWillUnmount() {
    StoreUser.removeChangeListener();
  }

  render() {
    const { userName } = this.state;
    return (
      <div key={userName}>
        {userName}
      </div>
    );
  }
  _onChange(){
    StoreUser.getStoredData(_callbackFunction.bind(this))
    function _callbackFunction (storedData) {
      // console.log('storedData', storedData)
      this.setState({
        userName: storedData.userName,
      })
    }
  }

}

这工作得非常好。但是现在,我在这种方法中遇到了很大的错误:
例如,WebApp 有一个带有用户名的侧边栏,它正在监听 StoreUser 以显示最新的用户名。而且,webapp 有一个组件(页面),其中包含用户的配置文件(包括 userName),它也在监听 StoreUser。如果我卸载这个组件,那么侧边栏也将停止监听 StoreUser。

我从 egghead.io 或其他一些教程中学到了这种方法 - 我不记得具体是在哪里。看来,我错过了一些关于如何管理商店听众的重要观点。您能否提出解决上述问题的简单方法?

我试图坚持原来的 Flux 架构,请不要给我任何 Flux 架构的替代实现建议。这是我唯一的问题。

问题是您在卸载时没有删除监听器。您必须非常小心地删除与您添加的完全相同的函数引用(两次传递 this.callback.bind(this) 不起作用,因为两次调用 return 不同的函数引用)。最简单的方法是:

//...
export default class User extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      userName: null,
    };
    // create fixed bound function reference we can add and remove
    // (like the auto-binding React.createComponent does for you).
    this._onChange = this._onChange.bind(this);
  }

  componentDidMount() {
    StoreUser.addChangeListener(this._onChange);
  }

  componentWillUnmount() {
    // make sure you remove your listener!
    StoreUser.removeChangeListener(this._onChange);
  }
//...

您的代码有几个奇怪之处:

  • 为什么 StoreUser.getStoredData 需要回调,而不只是 return 处理数据?使用 flux,您应该始终同步查询。如果需要获取数据,getter 可以触发 ajax 获取,return null,然后在 ajax 请求 return 时触发另一个操作,导致另一个 emitChange,因此最终用用户数据触发 re-render。
  • 您的商店似乎正在触发服务器写入。通常,在不断变化的情况下,这将是动作创建者而不是商店的责任,但我认为商店这样做并不是重大违规行为。