React/reflux 如何进行正确的异步调用
React/reflux how to do proper async calls
我最近开始学习 ReactJS,但我对异步调用感到困惑。
假设我有一个带有 user/pass 字段和登录按钮的登录页面。组件看起来像:
var Login = React.createClass({
getInitialState: function() {
return {
isLoggedIn: AuthStore.isLoggedIn()
};
},
onLoginChange: function(loginState) {
this.setState({
isLoggedIn: loginState
});
},
componentWillMount: function() {
this.subscribe = AuthStore.listen(this.onLoginChange);
},
componentWillUnmount: function() {
this.subscribe();
},
login: function(event) {
event.preventDefault();
var username = React.findDOMNode(this.refs.email).value;
var password = React.findDOMNode(this.refs.password).value;
AuthService.login(username, password).error(function(error) {
console.log(error);
});
},
render: function() {
return (
<form role="form">
<input type="text" ref="email" className="form-control" id="username" placeholder="Username" />
<input type="password" className="form-control" id="password" ref="password" placeholder="Password" />
<button type="submit" className="btn btn-default" onClick={this.login}>Submit</button>
</form>
);
}
});
AuthService 看起来像:
module.exports = {
login: function(email, password) {
return JQuery.post('/api/auth/local/', {
email: email,
password: password
}).success(this.sync.bind(this));
},
sync: function(obj) {
this.syncUser(obj.token);
},
syncUser: function(jwt) {
return JQuery.ajax({
url: '/api/users/me',
type: "GET",
headers: {
Authorization: 'Bearer ' + jwt
},
dataType: "json"
}).success(function(data) {
AuthActions.syncUserData(data, jwt);
});
}
};
操作:
var AuthActions = Reflux.createActions([
'loginSuccess',
'logoutSuccess',
'syncUserData'
]);
module.exports = AuthActions;
并存储:
var AuthStore = Reflux.createStore({
listenables: [AuthActions],
init: function() {
this.user = null;
this.jwt = null;
},
onSyncUserData: function(user, jwt) {
console.log(user, jwt);
this.user = user;
this.jwt = jwt;
localStorage.setItem(TOKEN_KEY, jwt);
this.trigger(user);
},
isLoggedIn: function() {
return !!this.user;
},
getUser: function() {
return this.user;
},
getToken: function() {
return this.jwt;
}
});
所以当我点击登录按钮时,流程如下:
Component -> AuthService -> AuthActions -> AuthStore
我直接用 AuthService.login
调用 AuthService。
我的问题是我做对了吗?
我应该使用 action preEmit 并执行以下操作:
var ProductAPI = require('./ProductAPI')
var ProductActions = Reflux.createActions({
'load',
'loadComplete',
'loadError'
})
ProductActions.load.preEmit = function () {
ProductAPI.load()
.then(ProductActions.loadComplete)
.catch(ProductActions.loadError)
}
问题在于 preEmit 会使组件的回调更加复杂。我想学习正确的方法并找到使用 ReactJS/Reflux 堆栈放置后端调用的位置。
我还发现 async with reflux 有点令人困惑。使用来自 facebook 的原始流量,我会做这样的事情:
var ItemActions = {
createItem: function (data) {
$.post("/projects/" + data.project_id + "/items.json", { item: { title: data.title, project_id: data.project_id } }).done(function (itemResData) {
AppDispatcher.handleViewAction({
actionType: ItemConstants.ITEM_CREATE,
item: itemResData
});
}).fail(function (jqXHR) {
AppDispatcher.handleViewAction({
actionType: ItemConstants.ITEM_CREATE_FAIL,
errors: jqXHR.responseJSON.errors
});
});
}
};
因此该操作会执行 ajax 请求,并在完成后调用调度程序。我也不喜欢 preEmit 模式,所以我只想使用商店中的处理程序:
var Actions = Reflux.createActions([
"fetchData"
]);
var Store = Reflux.createStore({
listenables: [Actions],
init() {
this.listenTo(Actions.fetchData, this.fetchData);
},
fetchData() {
$.get("http://api.com/thedata.json")
.done((data) => {
// do stuff
});
}
});
我不太喜欢在商店里做这件事,但考虑到回流如何将动作抽象化,并且会持续触发 listenTo 回调,我对此很满意。更容易理解我如何将回调数据设置到商店中。仍然保持单向。
我也在使用 Reflux,并且我使用不同的方法进行异步调用。
在 vanilla Flux 中,异步调用放在操作中。
但在 Reflux 中,异步代码在商店中效果最好(至少在我看来是这样):
因此,特别是在您的情况下,我将创建一个名为 'login' 的操作,该操作将由组件触发并由将启动登录过程的商店处理。握手结束后,商店将在组件中设置一个新状态,让它知道用户已登录。同时(例如 this.state.currentUser == null
时)组件可能会显示加载指示器。
对于 Reflux,您真的应该看看 https://github.com/spoike/refluxjs#asynchronous-actions。
上面描述的简短版本是:
不要使用 PreEmit 挂钩
使用异步操作
var MyActions = Reflux.createActions({
"doThis" : { asyncResult: true },
"doThat" : { asyncResult: true }
});
这不仅会创建 'makeRequest' 操作,还会创建 'doThis.completed'、'doThat.completed'、'doThis.failed' 和 'doThat.failed' 操作。
- (可选,但首选)使用承诺调用操作
MyActions.doThis.triggerPromise(myParam)
.then(function() {
// do something
...
// call the 'completed' child
MyActions.doThis.completed()
}.bind(this))
.catch(function(error) {
// call failed action child
MyActions.doThis.failed(error);
});
我们最近重写了我们所有的操作和 'preEmit' 这个模式的挂钩,并且喜欢结果和生成的代码。
我最近开始学习 ReactJS,但我对异步调用感到困惑。
假设我有一个带有 user/pass 字段和登录按钮的登录页面。组件看起来像:
var Login = React.createClass({
getInitialState: function() {
return {
isLoggedIn: AuthStore.isLoggedIn()
};
},
onLoginChange: function(loginState) {
this.setState({
isLoggedIn: loginState
});
},
componentWillMount: function() {
this.subscribe = AuthStore.listen(this.onLoginChange);
},
componentWillUnmount: function() {
this.subscribe();
},
login: function(event) {
event.preventDefault();
var username = React.findDOMNode(this.refs.email).value;
var password = React.findDOMNode(this.refs.password).value;
AuthService.login(username, password).error(function(error) {
console.log(error);
});
},
render: function() {
return (
<form role="form">
<input type="text" ref="email" className="form-control" id="username" placeholder="Username" />
<input type="password" className="form-control" id="password" ref="password" placeholder="Password" />
<button type="submit" className="btn btn-default" onClick={this.login}>Submit</button>
</form>
);
}
});
AuthService 看起来像:
module.exports = {
login: function(email, password) {
return JQuery.post('/api/auth/local/', {
email: email,
password: password
}).success(this.sync.bind(this));
},
sync: function(obj) {
this.syncUser(obj.token);
},
syncUser: function(jwt) {
return JQuery.ajax({
url: '/api/users/me',
type: "GET",
headers: {
Authorization: 'Bearer ' + jwt
},
dataType: "json"
}).success(function(data) {
AuthActions.syncUserData(data, jwt);
});
}
};
操作:
var AuthActions = Reflux.createActions([
'loginSuccess',
'logoutSuccess',
'syncUserData'
]);
module.exports = AuthActions;
并存储:
var AuthStore = Reflux.createStore({
listenables: [AuthActions],
init: function() {
this.user = null;
this.jwt = null;
},
onSyncUserData: function(user, jwt) {
console.log(user, jwt);
this.user = user;
this.jwt = jwt;
localStorage.setItem(TOKEN_KEY, jwt);
this.trigger(user);
},
isLoggedIn: function() {
return !!this.user;
},
getUser: function() {
return this.user;
},
getToken: function() {
return this.jwt;
}
});
所以当我点击登录按钮时,流程如下:
Component -> AuthService -> AuthActions -> AuthStore
我直接用 AuthService.login
调用 AuthService。
我的问题是我做对了吗?
我应该使用 action preEmit 并执行以下操作:
var ProductAPI = require('./ProductAPI')
var ProductActions = Reflux.createActions({
'load',
'loadComplete',
'loadError'
})
ProductActions.load.preEmit = function () {
ProductAPI.load()
.then(ProductActions.loadComplete)
.catch(ProductActions.loadError)
}
问题在于 preEmit 会使组件的回调更加复杂。我想学习正确的方法并找到使用 ReactJS/Reflux 堆栈放置后端调用的位置。
我还发现 async with reflux 有点令人困惑。使用来自 facebook 的原始流量,我会做这样的事情:
var ItemActions = {
createItem: function (data) {
$.post("/projects/" + data.project_id + "/items.json", { item: { title: data.title, project_id: data.project_id } }).done(function (itemResData) {
AppDispatcher.handleViewAction({
actionType: ItemConstants.ITEM_CREATE,
item: itemResData
});
}).fail(function (jqXHR) {
AppDispatcher.handleViewAction({
actionType: ItemConstants.ITEM_CREATE_FAIL,
errors: jqXHR.responseJSON.errors
});
});
}
};
因此该操作会执行 ajax 请求,并在完成后调用调度程序。我也不喜欢 preEmit 模式,所以我只想使用商店中的处理程序:
var Actions = Reflux.createActions([
"fetchData"
]);
var Store = Reflux.createStore({
listenables: [Actions],
init() {
this.listenTo(Actions.fetchData, this.fetchData);
},
fetchData() {
$.get("http://api.com/thedata.json")
.done((data) => {
// do stuff
});
}
});
我不太喜欢在商店里做这件事,但考虑到回流如何将动作抽象化,并且会持续触发 listenTo 回调,我对此很满意。更容易理解我如何将回调数据设置到商店中。仍然保持单向。
我也在使用 Reflux,并且我使用不同的方法进行异步调用。
在 vanilla Flux 中,异步调用放在操作中。
但在 Reflux 中,异步代码在商店中效果最好(至少在我看来是这样):
因此,特别是在您的情况下,我将创建一个名为 'login' 的操作,该操作将由组件触发并由将启动登录过程的商店处理。握手结束后,商店将在组件中设置一个新状态,让它知道用户已登录。同时(例如 this.state.currentUser == null
时)组件可能会显示加载指示器。
对于 Reflux,您真的应该看看 https://github.com/spoike/refluxjs#asynchronous-actions。
上面描述的简短版本是:
不要使用 PreEmit 挂钩
使用异步操作
var MyActions = Reflux.createActions({
"doThis" : { asyncResult: true },
"doThat" : { asyncResult: true }
});
这不仅会创建 'makeRequest' 操作,还会创建 'doThis.completed'、'doThat.completed'、'doThis.failed' 和 'doThat.failed' 操作。
- (可选,但首选)使用承诺调用操作
MyActions.doThis.triggerPromise(myParam)
.then(function() {
// do something
...
// call the 'completed' child
MyActions.doThis.completed()
}.bind(this))
.catch(function(error) {
// call failed action child
MyActions.doThis.failed(error);
});
我们最近重写了我们所有的操作和 'preEmit' 这个模式的挂钩,并且喜欢结果和生成的代码。