Reputation: 4850
How to setState() the response received from an AJAX Request so that I can display them in the page?
constructor(props)
{
super(props);
this.state = {
email: '',
first_name: '',
middle_name: '',
country: '',
country_code: '',
mobile_number: '',
gender: ''
}
}
componentDidMount()
{
store.dispatch(getUserProfile())
.then(() => {
const user = this.props.userProfile.userProfile && this.props.userProfile.userProfile.data.data;
this.setState({
email: user.email,
first_name: user.first_name
});
})
}
render()
{
return (
<div className="form-group col-sm-12">
<label htmlFor="email">Email*</label>
<input type="email" name="email" value={this.state.email || ''}/>
</div>
<div className="form-group col-sm-12">
<label htmlFor="email">First Name*</label>
<input type="email" name="email" value={this.state.first_name || ''}/>
</div>
)
}
Apparently, I can't use .then() with store.dispatch method.
Uncaught TypeError: _store2.default.dispatch(...).then is not a function
getUserProfile() action function
import axios from 'axios';
export function getUserProfile()
{
return function(dispatch)
{
dispatch(userProfileSuccess(false));
dispatch(userProfileError(null));
const request = axios
({
url: "http://testapi/auth/v1/user/details",
method: "get",
headers: {
'Content-Type': 'application/json',
'Authorization' : 'Bearer ' + localStorage.getItem('access_token')
}
})
.then(function(response) { dispatch(userProfileSuccess(response)); })
.catch(function(error) {
console.log(error)
});
return {
type: 'USER_PROFILE_SUCCESS',
payload: request
}
};
}
function userProfileSuccess(userProfile)
{
return {
type: 'USER_PROFILE_SUCCESS',
userProfile: userProfile
};
}
function userProfileError(userProfileError)
{
return {
type: 'USER_PROFILE_ERROR',
userProfileError: userProfileError
};
}
export default getUserProfile;
In the AJAX call, I tried:
.then(function(response) {
return new Promise((resolve) => {
dispatch(userProfileSuccess(response));
resolve();
});
})
but the console reports the same error.
Is there a callback that I can pass to store.dispatch
? What is the correct approach to this?
Upvotes: 2
Views: 2726
Reputation: 16472
As you are using redux then your redux store should keep track about when the api call is in progress or has completed or caught some error. So instead of passing any callback or promise, you should dispatch an action for each event like processing, success, error etc (which you are already doing in getprofile function). Though i would say you nicely distinguish between process, success, error. For example you getprofile method should roughly look like this
export function getUserProfile() {
return function (dispatch) {
dispatch(userProfileProcessing())
const request = axios({
url: "http://testapi/auth/v1/user/details",
method: "get",
headers: {
'Content-Type': 'application/json',
'Authorization' : 'Bearer ' + localStorage.getItem('access_token'),
},
})
.then(function (response) {
dispatch(userProfileSuccess(response))
})
.catch(function (error) {
dispatch(userProfileError(response))
console.log(error)
});
};
}
It is just what i prefer. If you want it your way, that is also fine.
Now everytime you dispatch any action, redux will update the reducer state. So thats the place where you can set/reset some flag to make the component aware of what is going on with api call. So your reducer might look like this:
// getUserProfileReducer.js
userProfileReducer = (state = {}, action) => {
switch (action.type) {
case 'USER_PROFILE_PROCESSING':
return {
...state,
processing: true,
success: false,
fail: false,
userProfile: null,
}
case 'USER_PROFILE_SUCCESS':
return {
...state,
processing: false,
success: true,
fail: false,
userProfile: action.userProfile,
}
case 'USER_PROFILE_Error':
return {
...state,
processing: false,
success: false,
fail: true,
userProfile: null,
}
}
}
Now all you need to do is to access this state from you component so that you can take necessary action according to that. For that you can user mapStateToProps function which convert the redux state to prop of the component.
constructor(props) {
super(props)
this.state = {
email: '',
first_name: '',
middle_name: '',
country: '',
country_code: '',
mobile_number: '',
gender: '',
}
}
componentWillReceiveProps(newProps) {
if (newProps.userProfileStatus.success) {
// The success flag is true so set the state
const user = newProps.userProfileStatus
this.setState({
email: user.email,
first_name: user.first_name,
})
}
else if (newProps.userProfileStatus.processing) {
// Api call is in progress so do action according to that like show loader etc.
}
}
componentDidMount() {
store.dispatch(getUserProfile())
}
render() {
return (
...
)
}
const mapStateToProps = (state) => {
return {
userProfileStatus: state.userProfileReducer,
}
}
Upvotes: 1
Reputation: 962
You can add a callback in componentDidMount()
componentDidMount()
{
store.dispatch(getUserProfile(), () => {
const user = this.props.userProfile.userProfile && this.props.userProfile.userProfile.data.data;
this.setState({
email: user.email,
first_name: user.first_name
});
})
}
This may not run exactly same, I just want to give you an idea how to add callback using arrow function so that you don't need to use then.
Upvotes: 2
Reputation: 1326
Redux stores the state in the Redux store, separately from the React component state (think setState). You are almost there. What you need to do is guide the result data from the async dispatch to the redux store and then to your local component state. Steps 3 and 4 below.
connect
function.Upvotes: 1