我如何处理 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。
- 您的商店似乎正在触发服务器写入。通常,在不断变化的情况下,这将是动作创建者而不是商店的责任,但我认为商店这样做并不是重大违规行为。
这是平均商店:
// 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。
- 您的商店似乎正在触发服务器写入。通常,在不断变化的情况下,这将是动作创建者而不是商店的责任,但我认为商店这样做并不是重大违规行为。