How to better update the view based on API response. Let's say we have a setup where user should be redirected to dashboard view when successfully logs in and an alert message is displayed for any error on the login screen.
What I currently use
I am used to call the action creator to finally hit the endpoint for a response. And based on that response dispatch a relevant success or failure action
to update my global state which I subscribe in my component using connect(mapStateToProps, mapActionsToProps)
.
Problem
Let's say I have a loginSuccess
in my global state and I use that for redirection or error handling in my Login component. For instance, I want to also handle response for a Password Reset screen as well. So I store another information in my global state and use it in relevant component.
Is that even a problem?
I have a feeling that it's too much information to store in global state.
What I read new?
So I started reading about passing a callback
to my action creator which handles the response. This way the error handling logic stays local to the component.
Example
Login.js
componentDidMount(){
this.props.login({
data, // login payload.
callback: (response) => this.handleResponse(response)
})
}
// response handler.
handleResponse = response => {
this.setState({isError: response.isError});
}
render () {
/* Use the this.state.isError to determine either to redirect or show error */
}
Actions.js
export const login = payload => ({
type: ATTEMPT_LOGIN,
payload
})
saga.js
// login handler in saga.
function* loginAttempt({payload}) {
const {data, callback} = payload;
try {
const resp = yield apiCaller.call(method, url, data, headers); // utitly to fetch data and return a standard response.
callback(resp);
}catch(error){
callback(DEFAULT_ERROR_RESPONSE); // {isError: true, message: 'Something went wrong'}
}
}
Which one is better?
Is there any better way to handle this sort of stuff? How do you guys handle it?
What you call "global" state is a design decision that Redux made to have a single store. It's okay to add as many loginSuccess
properties as you need to the store. To keep them organized you use combineReducers
to divide your store into slices.
You are already using Redux and Redux Saga. You shouldn't have to use a callback for something like your problem. Let the reducers, sagas, and selectors take care of the flow:
function* loginAttempt(payload) {
try {
const response = yield apiCaller.call(method, url, data, headers);
yield put(LOGIN_SUCCESS, response);
}catch(error){
yield put(LOGIN_ERROR, error);
}
}
The default state can be something like:
user: null,
loginRequestStatus: {
inProgress: false,
error: false,
}
The reducer for ATTEMPT_LOGIN
should set it in progress. The reducer for LOGIN_SUCCESS
should set the user. LOGIN_ERROR
sets the error. Make selectors like selectAuthUser
and selectIsLoginInProgress
that your components can use. If you're using a router that can talk to Redux, like router5
, you can even redirect from the Saga.