Curious how Snowflake is handling Error Alerts
Closed this issue · 4 comments
I was looking through the repository, and was curious how it is displaying the right Error Alerts. It seems like both Register and Login use LoginRender, which shows ErrorAlert during render whenever it receives an error prop.
My question is how does Snowflake know not to display multiple alerts?? Here is the issue I'm facing:
I used to have my own Register and Login components under react-native-router-flux. It seems like whenever a redux state changes, ALL the components under the Router gets rerendered, not just the "current" one. So when a registration fails, for example, I see alerts NOT just from Register, but from all components such as Login.
Here's an example:
main.ios.js
export default class App extends Component {
render() {
return (
<Provider store={store}>
<Router>
<Scene key='root'>
<Scene key='register' component={Register} type='replace'>
<Scene key='signin' component={SignIn} type='replace'>
</Scene>
</Router>
</Provider>
);
}
}
Register.js
class Register extends Component {
render() {
const { loading, error } = this.props;
if (!loading && error) {
Alert.alert('Error from REGISTER');
}
return <View>...</View>;
}
}
const mapStateToProps = (state) => {
return {
loading: state.get("authenticationReducer").get("loading"),
error: state.get("authenticationReducer").get("error"),
};
};
export default connect(mapStateToProps)(Register);
SignIn.js
class SignIn extends Component {
render() {
const { loading, error } = this.props;
if (!loading && error) {
Alert.alert('Error from SIGNIN');
}
return <View>...</View>;
}
}
const mapStateToProps = (state) => {
return {
loading: state.get("authenticationReducer").get("loading"),
error: state.get("authenticationReducer").get("error"),
};
};
export default connect(mapStateToProps)(SignIn);
Whenever a Registration fails and the "error" prop in the redux state updates, both SignIn and Register rerenders since they are both "connected" to flux, and I get 2 popups, one from Register and another from Login. Seems like you guys are taking a similar approach - handling errors&alerting in the render. How did you guys resolve this issue? Thanks!
Hi @yoongeemin. Just to be clear, I believe you are seeing multiple reducers
firing not because of the router
but rather a design principal of redux
. Redux invokes all the reducers w/ the current state and action. Most of the reducers will end up just returning the state as they don't recognize the action.
In the case of Login
, in LoginRender
, the specific error from auth
is checked with this.errorAlert.checkError(this.props.auth.form.error)
. So the question is then, where did this prop
get populated? First, look at Login
, where the action login
is invoked:
function buttonPressHandler (login, username, password) {
login(username, password)
}
The authActions
calls this:
return BackendFactory().login({
username: username,
password: password
})
and it results in either a success or it has this:
.catch((error) => {
dispatch(loginFailure(error))
})
Now that error
object is what's thrown from sub-class of BackendFactory
, for example, Hapi
, where the login
function is executed. Note that it has a catch
of error
:
.catch((error) => {
throw (error)
})
Back in authActions
, that error
object is dispatched:
.catch((error) => {
dispatch(loginFailure(error))
})
In authReducer
, the following code sets the error
case SIGNUP_FAILURE:
case LOGOUT_FAILURE:
case LOGIN_FAILURE:
case RESET_PASSWORD_FAILURE:
return state.setIn(['form', 'isFetching'], false)
.setIn(['form', 'error'], action.payload)
Note the last line. That's the specific error for LOGIN_FAILURE
and thus LoginRender
can process that particular error with the following:
// display the login / register / change password screens
this.errorAlert.checkError(this.props.auth.form.error)
I'm not sure that I'm answering your question but this is how Snowflake knows what specific error occurred. All the actions that are dispatched either have a SUCCESS
or FAILURE
resultant action.
@bartonhammond Thanks so much for the response. Now I understand a little more about the redux flow.
However I guess my question is: when the login fails and the authReducer updates its auth.form.error, doesn't that state change get propagated to ALL the components "connected" to authReducer (listening for change in auth.form.error)?
For example, Login, Register, ForgotPassword, and Logout each render instance of LoginRender. So shouldn't we be seeing 4 error alerts at the same time?? That's the exact problem I'm facing: i am seeing 2 alerts, one from Login and one from Register..
authReducer only has "error" state and doesnt distinguish between loginError, registerError, or logoutError, right? So I guess my question is when "error" gets updated in authReducer, how do you tell only ONE component to respond to that state change, instead of all 4 components that listen to changes in the "error" state?
Not sure if I explained that right :( much thanks amd appreciate your guidance
However I guess my question is: when the login fails and the authReducer updates its auth.form.error, doesn't that state change get propagated to ALL the components "connected" to authReducer?
No! I think you are confusing component
vs reducer
. Redux will pass each action
and current state to all the reducers but only one reducer, authReducer
,handles the action type of LOGIN_FAILURE
! The only component
processing is the one currently displayed.
I'm not sure how you are handling the propagation of the errors but in Snowflake they are at a fine level - the errors are tied to a specific action.
authReducer only has "error" and doesnt distinguish between logimError, registerError, or logoutError, right? So I guess my question is when "error" gets updated in authReducer, how do you tell only ONE component to respond to that state change??
True! authReducer
processes certain actions
the same:
case SIGNUP_FAILURE:
case LOGOUT_FAILURE:
case LOGIN_FAILURE:
case RESET_PASSWORD_FAILURE:
return state.setIn(['form', 'isFetching'], false)
.setIn(['form', 'error'], action.payload)
If I understand your question, there's only one component "live" at the time. For example, Register
calls the authActions.signup
method which dispatches a SIGNUP_FAILURE
which LoginRender
then displays.
Does that help?
I believe this has been answered