I recently started to learn ReactJS, but I'm getting confused for async calls.
Lets say I have a Login page with user/pass fields and login button. Component looks like:
var Login = React.createClass({
getInitialState: function() {
return {
isLoggedIn: AuthStore.isLoggedIn()
};
},
onLoginChange: function(loginState) {
this.setState({
isLoggedIn: loginState
});
},
ponentWillMount: function() {
this.subscribe = AuthStore.listen(this.onLoginChange);
},
ponentWillUnmount: 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 looks like:
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);
});
}
};
Actions:
var AuthActions = Reflux.createActions([
'loginSuccess',
'logoutSuccess',
'syncUserData'
]);
module.exports = AuthActions;
And store:
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;
}
});
So when I click the login button the flow is the following:
Component -> AuthService -> AuthActions -> AuthStore
I'm directly calling AuthService with AuthService.login
.
My question is I'm I doing it right?
Should I use action preEmit and do:
var ProductAPI = require('./ProductAPI')
var ProductActions = Reflux.createActions({
'load',
'loadComplete',
'loadError'
})
ProductActions.load.preEmit = function () {
ProductAPI.load()
.then(ProductActions.loadComplete)
.catch(ProductActions.loadError)
}
The problem is the preEmit is that it makes the callback to ponent more plex. I would like to learn the right way and find where to place the backend calls with ReactJS/Reflux stack.
I recently started to learn ReactJS, but I'm getting confused for async calls.
Lets say I have a Login page with user/pass fields and login button. Component looks like:
var Login = React.createClass({
getInitialState: function() {
return {
isLoggedIn: AuthStore.isLoggedIn()
};
},
onLoginChange: function(loginState) {
this.setState({
isLoggedIn: loginState
});
},
ponentWillMount: function() {
this.subscribe = AuthStore.listen(this.onLoginChange);
},
ponentWillUnmount: 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 looks like:
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);
});
}
};
Actions:
var AuthActions = Reflux.createActions([
'loginSuccess',
'logoutSuccess',
'syncUserData'
]);
module.exports = AuthActions;
And store:
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;
}
});
So when I click the login button the flow is the following:
Component -> AuthService -> AuthActions -> AuthStore
I'm directly calling AuthService with AuthService.login
.
My question is I'm I doing it right?
Should I use action preEmit and do:
var ProductAPI = require('./ProductAPI')
var ProductActions = Reflux.createActions({
'load',
'loadComplete',
'loadError'
})
ProductActions.load.preEmit = function () {
ProductAPI.load()
.then(ProductActions.loadComplete)
.catch(ProductActions.loadError)
}
The problem is the preEmit is that it makes the callback to ponent more plex. I would like to learn the right way and find where to place the backend calls with ReactJS/Reflux stack.
Share Improve this question edited Jan 30, 2016 at 10:45 Dmitry Shvedov 3,2964 gold badges40 silver badges56 bronze badges asked Jun 12, 2015 at 15:18 DeepsyDeepsy 3,8108 gold badges42 silver badges72 bronze badges3 Answers
Reset to default 6I am using Reflux as well and I use a different approach for async calls.
In vanilla Flux, the async calls are put in the actions.
But in Reflux, the async code works best in stores (at least in my humble opinion):
So, in your case in particular, I would create an Action called 'login' which will be triggered by the ponent and handled by a store which will start the login process. Once the handshaking ends, the store will set a new state in the ponent that lets it know the user is logged in. In the meantime (while this.state.currentUser == null
, for example) the ponent may display a loading indicator.
For Reflux you should really take a look at https://github./spoike/refluxjs#asynchronous-actions.
The short version of what is described over there is:
Do not use the PreEmit hook
Do use asynchronous actions
var MyActions = Reflux.createActions({
"doThis" : { asyncResult: true },
"doThat" : { asyncResult: true }
});
This will not only create the 'makeRequest' action, but also the 'doThis.pleted', 'doThat.pleted', 'doThis.failed' and 'doThat.failed' actions.
- (Optionally, but preferred) use promises to call the actions
MyActions.doThis.triggerPromise(myParam)
.then(function() {
// do something
...
// call the 'pleted' child
MyActions.doThis.pleted()
}.bind(this))
.catch(function(error) {
// call failed action child
MyActions.doThis.failed(error);
});
We recently rewrote all our actions and 'preEmit' hooks to this pattern and do like the results and resulting code.
I also found async with reflux kinda confusing. With raw flux from facebook, i would do something like this:
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
});
});
}
};
So the action does the ajax request, and invokes the dispatcher when done. I wasn't big on the preEmit pattern either, so i would just use the handler on the store instead:
var Actions = Reflux.createActions([
"fetchData"
]);
var Store = Reflux.createStore({
listenables: [Actions],
init() {
this.listenTo(Actions.fetchData, this.fetchData);
},
fetchData() {
$.get("http://api./thedata.json")
.done((data) => {
// do stuff
});
}
});
I'm not big on doing it from the store, but given how reflux abstracts the actions away, and will consistently fire the listenTo callback, i'm fine with it. A bit easier to reason how i also set call back data into the store. Still keeps it unidirectional.