Best practice to handle errors in Redux and React - javascript

I have an asynchronous function in my redux actions and it returns an object like this in my reducer:
{
user: {},
fetching: false,
fetched, false,
error: null
}
So basically when I start calling the asynchronous function I update the redux state to fetching: true and if the result is successfully fulfilled then fetched:true and fetching:false
So I map the redux state to props using connect in react-redux and I added this in my component's render function:
if(this.props.fetched) {
// Go to next page
}
And it automatically goes to the next page once the data is fetched.
However, I have a problem with my error handling. When error changes from null to error then how do I handle the error handling on my react component. What I have right now is:
if(this.props.error != null) {
// popup error
}
But now I end up in a situation that next time I call my action it already has this.props.error assigned to an error and its not null which results it displaying the popup even if there is no error.
Do I have to reset my error everytime it is displayed or is there any better practice of doing this whole thing?

You can use the redux-catch middleware to capture the errors for Redux reducers and middlewares.
You can use something like,
import reduxCatch from 'redux-catch';
function errorHandler(error, getState, lastAction, dispatch) {
//dispatch ERROR action as you need.
}
const store = createStore(reducer, applyMiddleware(
reduxCatch(errorHandler)
));
And, display your Error Popup when you receive the ERROR action which is triggered from the redux-catch middleware. When you close the popup, dispatch an action which resets the error to null so that popup would not be displayed if there are no errors to display.

I had a similar problem when I had a modal continue to display old errors after I closed the modal. I solved it by dispatching a "resetErrors" action after I received a success through my callback.
I have not come across a better solution other than "reseting" it every time.

Related

Is there a well-established way to update local state immediately without waiting for an API response in React/Redux?

TL;DR: Is there some well-known solution out there using React/Redux for being able to offer a snappy and immediately responsive UI, while keeping an API/database up to date with changes that can gracefully handle failed API requests?
I'm looking to implement an application with a "card view" using https://github.com/atlassian/react-beautiful-dnd where a user can drag and drop cards to create groups. As a user creates, modifies, or breaks up groups, I'd like to make sure the API is kept up to date with the user's actions.
HOWEVER, I don't want to have to wait for an API response to set the state before updating the UI.
I've searched far and wide, but keep coming upon things such as https://redux.js.org/tutorials/fundamentals/part-6-async-logic which suggests that the response from the API should update the state.
For example:
export default function todosReducer(state = initialState, action) {
switch (action.type) {
case 'todos/todoAdded': {
// Return a new todos state array with the new todo item at the end
return [...state, action.payload]
}
// omit other cases
default:
return state
}
}
As a general concept, this has always seemed odd to me, since it's the local application telling the API what needs to change; we obviously already have the data before the server even responds. This may not always be the case, such as creating a new object and wanting the server to dictate a new "unique id" of some sort, but it seems like there might be a way to just "fill in the blanks" once the server does response with any missing data. In the case of an UPDATE vs CREATE, there's nothing the server is telling us that we don't already know.
This may work fine for a small and lightweight application, but if I'm looking at API responses in the range of 500-750ms on average, the user experience is going to just be absolute garbage.
It's simple enough to create two actions, one that will handle updating the state and another to trigger the API call, but what happens if the API returns an error or a network request fails and we need to revert?
I tested how Trello implements this sort of thing by cutting my network connection and creating a new card. It eagerly creates the card immediately upon submission, and then removes the card once it realizes that it cannot update the server. This is the sort of behavior I'm looking for.
I looked into https://redux.js.org/recipes/implementing-undo-history, which offers a way to "rewind" state, but being able to implement this for my purposes would need to assume that subsequent API calls all resolve in the same order that they were called - which obviously may not be the case.
As of now, I'm resigning myself to the fact that I may need to just follow the established limited pattern, and lock the UI until the API request completes, but would love a better option if it exists within the world of React/Redux.
The approach you're talking about is called "optimistic" network handling -- assuming that the server will receive and accept what the client is doing. This works in cases where you don't need server-side validation to determine if you can, say, create or update an object. It's also equally easy to implement using React and Redux.
Normally, with React and Redux, the update flow is as follows:
The component dispatches an async action creator
The async action creator runs its side-effect (calling the server), and waits for the response.
The async action creator, with the result of the side-effect, dispatches an action to call the reducer
The reducer updates the state, and the component is re-rendered.
Some example code to illustrate (I'm pretending we're using redux-thunk here):
// ... in my-component.js:
export default () => {
const dispatch = useDispatch();
useEffect(() => {
dispatch(MyActions.UpdateData(someDataFromSomewhere));
});
return (<div />);
};
// ... in actions.js
export const UpdateData = async (data) => (dispatch, getStore) => {
const results = await myApi.postData(data);
dispatch(UpdateMyStore(results));
};
However, you can easily flip the order your asynchronous code runs in by simply not waiting for your asynchronous side effect to resolve. In practical terms, this means you don't wait for your API response. For example:
// ... in my-component.js:
export default () => {
const dispatch = useDispatch();
useEffect(() => {
dispatch(MyActions.UpdateData(someDataFromSomewhere));
});
return (<div />);
};
// ... in actions.js
export const UpdateData = async (data) => (dispatch, getStore) => {
// we're not waiting for the api response anymore,
// we just dispatch whatever data we want to our reducer
dispatch(UpdateMyStore(data));
myApi.postData(data);
};
One last thing though -- doing things this way, you will want to put some reconciliation mechanic in place, to make sure the client does know if the server calls fail, and that it retries or notifies the user, etc.
The key phrase here is "optimistic updates", which is a general pattern for updating the "local" state on the client immediately with a given change under the assumption that any API request will succeed. This pattern can be implemented regardless of what actual tool you're using to manage state on the client side.
It's up to you to define and implement what appropriate changes would be if the network request fails.

redux error handling for fetchs

I'm working on error handling w/ fetch's in redux. Do people typically write reducers/actions error handlers for every single request like the videos from redux.js.org? I was thinking about dispatching the same action w/ an error flag to handle errors, but I don't know if that's the right way to go about it.
In my experience, yes. I ordinarily have actions like GET_THINGS, GET_THINGS_SUCCESS, and GET_THINGS_ERROR, and then have a case statement in the reducer to handle each action. Most React devs I know do it the same well. In general, I would suggest following that pattern. That said, it does result in you having to handle a lot of GET actions and ERROR actions basically the same way, since you'll likely just be adding something like { Loading: true } or { Error: true } to the state...
For error handling and posting / putting data I use a generic way of raising a dialog for the user to indicate success or failure.
e.g. have a NoificationsAction
const NotificationAction = (open = false, message = '', status = 'success') => ({type: NOTIFICATION, payload: {open, message, status}})
and some dilaog component which raises a dialog or snackBar to display a message
Dispatch this when your request fails
myRequest()
.then((response) => {
if (response.status === 200) {
dispatch(projectsSuccess(response.data))
}
})
.catch((error) => {
dispatch(NotificationAction(true, `My Error Text : ${error}`, 'error'))
})
This way you reduce your boiler plate code in reducers

Redux-auto: action chaining vs index reducer

Can anyone tell me what the difference between using chain on a reducer function and doing work in the main index reducer function in redux-auto
I want to save an error,
A) store/chat/send.js
import actions from 'redux-auto'
//...
function rejected(chat, payload, error){
return chat;
} onError.chain = (c, p, error) => actions.logger.save(error)
//...
or
B) store/logger/index.js
import actions from 'redux-auto'
import save from './save'
export default function (errorsLog = [], action)
{
if(action.type == actions.chat.send.rejected){
return save(errorsLog,action.payload)
}
return errorsLog
}
They both work
My questions:
I don't know what would be better. What is the difference?
Why would I use one over the other?
Also, can't I just call the action logger.save(...) inside the
rejected. Why does this chain feature exist?
Thanks for any help :)
A) Using the chain(OnError) will fire the action AFTER the source(rejected) reducer has completed. creating a new call across you store.
B) You are changing the state in the source reducer call
Your qustions:
1,2) Using chaining will make you code more readable as the next function is collocated with the source reducer, but but having it in the index group all action that will happen to that part of the store.
3) Calling an action function directly within a reducer function. Is an anti-pattern. This is dispatching an action in the middle of a dispatched action. The reducer will be operating on inconsistent data.
One of the main Redux point is predictability. We should use as more pure functions as possible. The reducer must not have any side-effects at all.
Recently I've worked on the same feature - error (user action, etc) logging. I think all of this actions are side-effects. They have no profit for user and can't be a part of main business logic.
That's why I use custom middleware to capture all actions I need to log. The action which I need to log I've marked with some meta-prop (ex. {log: 'errorLog'}) and in the middleware I checked every action. If it has a log prop then I make some logger magic.
Finally I've got clearly understandable code where all of logging side-effects incapsulated in middleware.

Meteor how to wait on callback before dispatching Redux action

Meteor uses callbacks, but it seems like there is no 'wait' for them with Redux actions. So, if using something like Redux and having an action something like this:
export function loginWithPassword(email, password) {
return dispatch => {
Meteor.loginWithPassword(email, password, error => {
if (error) {
return dispatch({type: ASYNC_ERROR, data: error.reason})
});
}
}
... the action will complete and return before Meteor's call does (thus not re-rendering the UI if needed). I have scoured for hours trying to find the best way to deal with this to no avail. Based on what I have been able to find, it would seem the options are Meteor Futures or Promises, however I cannot find anything indicating which might be better or why.
I believe that Futures can only be server side and are less widely used than Promises, so Promises may be the better options based on that. I am not even sure if Futures is an option since actions are client side. But I am also not sure if Meteor will play nice with Promises given it's general sync nature.
Promises I believe could be either client or server. Assuming they were client, would it be better to put them in the actions or in the UI code dispatching the actions - or does it really matter?
Does anyone have any thoughts or insight into how best to handle something like this? A working example of one or the other (or both) would be great because I sure couldn't find one after hours of searching. Specifically it would be great to see an example of a simple Meteor login with password dispatched via Redux that displays a 'User not found" or some other 'async' error in the UI login form (the FIRST time).
The action above actually works, but it returns before Meteor is done returning the error, so the error is not displayed in the UI the initial time.
TIA!
P.S. There are a couple of examples out there that display the error in an alert window or console.log it - but that is not the same thing as updating/displaying it as a prop in the UI that dispatched the action. There are no prop re-renders tied to an alert or console.log. The whole idea is to show the error in the form the user is currently on (login form in this example case)
Using redux-thunk, you could write something like below. I think the trick here is knowing the status of the user object once it's logged in. One way of doing this is to use store.dispatch inside a Tracker.autorun:
// whenever the reactive data changes, we will dispatch the appropriate action
Tracker.autorun(() => {
if (Meteor.loggingIn()) {
return store.dispatch({
type: LOGGING_IN,
});
}
const user = Meteor.user();
store.dispatch({
type: SET_USER,
user,
}
});
export const login = ({ email, password }) => (dispatch) => {
Meteor.loginWithPassword(email, password, (error) => {
if (error) {
return dispatch({
type: LOGIN_ERROR,
error,
});
}
return dispatch({
type: LOGIN_SUCCESS,
});
});
};
You could imagine a user reducer that has a state like {user: {loggingIn: Boolean, authError: SomeErrorModel}}} or something like that
You could clear the authError on LOGGING_IN, and add it on LOGIN_ERROR
For any UI changes based on this reducer, just connect your react component with react-redux's connect()

Enforce an action to be called before another using Redux

Recently I did that for one app and I'd like to know if this would be considered a bad practice or if it is ok. Let's say I have a reducer listening for two actions:
switch (action.type) {
case 'PRE_FETCH_ACTION':
return Object.assign({}, state, {value: action.value, isAllowed: true})
case 'FETCHED_SUCCESS':
if (!state.isAllowed) {
throw Error('You did not dispatch PRE_FETCH_ACTION before fetching!');
}
return Object.assign({}, state, {data: action.data, isAllowed: false})
}
So the flow is:
I dispatch PRE_FETCH_ACTION
I make a fetch call to an external API
When the service response returns, it dispatches FETCHED_SUCCESS
If somebody tries to fetch data without dispatching the PRE_FETCH_ACTIONfirst, the code will throw an error.
Ok, so this works just fine. My concern is, as I said, if this would be considered a bad pattern. Why do I think so? Because the isAllowed piece of state is kinda internal to the reducer and it does not affect any component render method.
I think it's ok to have such "technical" flags in your app state.
In your case you want to ensure that actions are dispatched in a specific order, which defines a particular behavior of your app (for example, displaying a loader on the screen when data-fetching starts). That's why I think your "technical" flag eventually manages your UI.
To let your reducer "reduce only", you could make your check before dispatching, by testing your state in action creator. This pattern is detailed in Redux's doc about async actions. You would do it in a "thunk" action creator that you would call instead of calling sync action "fetchedSuccess", like this:
function toFetchedSuccess(data) { // "thunk" action creator
return (dispatch, getState) => {
const state = getState(); // current state
if(!state.isAllowed) {
console.error(
'You did not dispatch PRE_FETCH_ACTION before fetching!');
return Promise.resolve(); // nothing to do
} else {
return dispatch(fetchedSuccess(data));
}
};
}

Categories