Correct way of error handling in React-Redux
I would say that neither of your initial ideas capture the whole picture. Idea 1 is just a callback. If you want to use a callback: useCallback
. Idea 2 will work and is preferable if you don't need to use redux. Sometimes you're better off using redux. Maybe you're setting form validity by checking none of the input fields have errors or something similar. Since we're on the topic of redux, let's assume that's the case.
Usually the best approach to error handling with redux is to have an error field in state that is then passed to an error component.
const ExampleErrorComponent= () => {
const error = useSelector(selectError);
if (!error) return null;
return <div className="error-message">{error}</div>;
}
The error component doesn't have to just display an error, it could also do side effects with useEffect
.
How the error is set/unset depends on your application. Let's use your phone number example.
1. If the validity check is a pure function, it can be done in the reducer.
You would then set or unset the error field in response to phone number change actions. In a reducer built with a switch statement it could look like this.
case 'PHONE_NUMBER_CHANGE':
return {
...state,
phoneNumber: action.phoneNumber,
error: isValidPhoneNumber(action.phoneNumber) ? undefined : 'Invalid phone number',
};
2. If errors are reported by the backend, dispatch error actions.
Let's say you're sending the phone number to a backend that does validation before it does something with the number. You can't know if the data is valid on the client side. You'll just have to take the server's word for it.
const handleSubmit = useCallback(
() => sendPhoneNumber(phoneNumber)
.then(response => dispatch({
type: 'PHONE_NUMBER_SUBMISSION_SUCCESS',
response,
}))
.catch(error => dispatch({
type: 'PHONE_NUMBER_SUBMISSION_FAILURE',
error,
})),
[dispatch, phoneNumber],
);
The reducer should then come up with an appropriate message for the error and set it.
Don't forget to unset the error. You can unset the error on a change action or when making another request depending on the application.
The two approaches I outlined are not mutually exclusive. You can use the first to display locally detectable errors and also use the second to display server side or network errors.
I would use a formik with yup validation. then, for server-side error i would use something like this:
import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Spinner } from "@blueprintjs/core";
export default ({ action, selector, component, errorComponent }) => {
const dispatch = useDispatch();
useEffect(() => {
dispatch(action());
}, [dispatch, action]);
const DispatchFetch = () => {
const { data, isRequesting, error } = useSelector(selector());
if (!isRequesting && data) {
const Comp = component;
return <Comp data={data}></Comp>;
} else if (error) {
if (errorComponent) {
const ErrorComp = errorComponent;
return <ErrorComp error={error}></ErrorComp>;
}
return <div>{error}</div>;
}
return <Spinner></Spinner>;
};
return <DispatchFetch></DispatchFetch>;
};