How to clear error message coming from redux actions - reactjs

I have an API, which may return an error. In the simplistic form, the component reads as below. The question is, when an error occurs, what is the steps to dismiss/clear the error message?
class LoginError extends Component {
render() {
return (
{
this.props.error != null ?
<h2>this.props.error</h2> :
undefined
}
)
}
}
const mapStateToProps = (state) => {
return {
error: state.error
};
}
export default connect(mapStateToProps, null)(LoginError);

There is no straight forward way to do this, you basically have two options: set the error to undefined in the reducer when another action fires, or provide a close button on the error itself using a reusable error component, that dispatches an action that will set the error to undefined in the reducer.
Personally i've always used the first method, lets take the submit of a form as an example. When the user submits the form you fire form-submit/request and maybe show a loading message. If the form is sent correctly you fire form-submit and if an error happens you fire form-submit/error, setting the error in the reducer. Then in form-submit/request you clear the error, so the user gets feedback if an error happens but if the form is submitted again the error is cleared. If you don't want to do anything when the form is submitted, which is odd, you can clear the error when form-submit is fired. The downside of this approach is that if for example you want to clear the error when any field of the form is changed, you'll have to add another action and make the error undefined for that action as well.
Now if you put a close button in the error component you can reuse the error React component, but you'll have to have a action/dismiss-error action for every API call and set the error to undefined on the reducer for each one of them, this can get very tedious quickly.
The best approach for me is to use the first method, but choosing carefully how much errors you display for each page or section. This way each page can have its error section that will be displayed for any API call that is associated with the page and you only need an error action for each page, not for every API call.

Quote from docs:
A store holds the whole state tree of your application. The only way
to change the state inside it is to dispatch an action on it.
That's it. If you keep something in redux state then to change it's value you have to dispatch an action.

Related

how to reset errors in a react redux application with a common error displaying component

I am new to redux so apologies if it's a stupid question. I have a react application and I am adding redux store to it. I am using thunk middleware to execute async code to get/save data from the backend.
I have this below in index.js:
store.dispatch(loadInitialState());
loadInitialState() calls backend service and loads the initial data and dispatches an action to load initial state:
dispatch({
type: LOAD_INITIAL_STATE,
payload: initData,
});
Now, in case things didn't go well I dispatch a set error action like this:
dispatch({ type: SET_ERROR_MESSAGE, payload: errorMessage });
and the reducer has got this:
case actions.SET_ERROR_MESSAGE:
return {
...state,
error: action.payload,
};
case actions.RESET_ERROR_MESSAGE:
return {
...state,
error: null,
};
I have a component called <DisplayError /> that I have included in App.js at the top where on every page of the application this component will be first one to render and it uses useSelector(state => state.error) to find if there and an error to display it.
The problem: If I have an error on a route/page and if I navigate away from that route the error message stays there because that component it common at the top of all pages. The only way I see to solve this is to do dispatch({ type: RESET_ERROR_MESSAGE, payload: null }); everywhere in the application to make sure that error message doesn't appear where it's not supposed to.
What is the way to reset error that set in redux state?
A few things...
The redux store has no relation to the routing. That's the whole point of the Redux storage, so you have data everywhere. If you have a component watching for the error message to be present in the store in all pages, either that component has to handle the route where to show which errors OR indeed you will have to call reset after the error message is displayed (or when navigating away).
So what's the right way to do it?
Well, the "right" way in my opinion is with callbacks, when talking about queries. This: react-query-v3.tanstack.com/guides/queries sets a pretty good standard IMO. Think about it this way: Errors are local instances of messages you need to show to the user. So you should show them, but not necessarily store them in your data store. So you use a callback, do the alerting in the way you want to and that's it.
"Ok, so the good practice is to keep all API calls within the component and then dispatch relevant redux actions?"
In my opinion, yes. Isolation and reusability are always in my mind at least.
"One case that I need to understand is there is a component that shows data from the db on the home page and in App.js I dispatch a thu..."
Short answer is: you should not be doing that in App.js but rather in a component specific to that data, where you would manage it's life-cycle, including errors.
This applies even if it's something that will be visible throughout the app.
Good hygiene to keep things isolated IMO.

In ReactJS i am getting this issue while rendering

The first image is about the error im getting and the second one is the API from which I need to show only vendor: store_name but while clicking on the update button it shows this error otherwise it remains okay and reloading manually only it works.
Unhandled Rejection (TypeError): Cannot read properties of undefined (reading 'store_address')
let cart_product_item = this.props.cart_product;
return (
{
cart_product_item && cart_product_item.length > 0 ?
cart_product_item[0].vendor.store_address : null
}
)
const mapStateToProps = (state) => {
return {
cart_product: state.homepageData.cart,
};
};
export default connect(mapStateToProps, {myCart})(Checkout);
The question does not provide all the information required to answer, but here is probably what happens:
You render a component
The component needs data from an API, and the data does not arrive immediately. The component renders before the data arrives.
Your component assumes the data is there immediately during first render, and thus throws an error when trying to access a property of an undefined object.
If the problem is indeed this, it can be overcome in many ways:
Make it appear loading if it does not have the data and when it does, display the data. This can be achieved e.g. by cart_product_item?.[0]?.vendor?.store_address || "Loading" or similar.
Do not render the component (= do not return anything other than e.g. null if the data is not there yet.

Passing data based on another api response from redux

I have an api which will list what all access do the current user have, so once the app.js loads I am calling the api in the componentWillMount, so basically I have three routes , home, userslist, eachuser. So the home is a static text page.
userslist is a component where I list all the users, once you click on the edit icon of user it will take you to the details of the user in the eachuser component.
The problem is since the calls are async once the useraccessApi resolves and gets only the data I should call the usersListApi , by passing the useraccessApi response.
What I mean by happy flow is first user loads the localhost:3000/home so the useraccessApi will call and the redux have data, so while switching to userslist tab on componenWillMount it will work. But if the user directly selects localhost:3000/userlist it will throw error on componenWillMount so moved the code to componentWillRecieveProps().
So how can I resolve this issue. Or should I use mergeProps to resolve it.
App.js
componenWillMount(){
this.props.userAccessApi()
}
UsersList
componentWillMount(){
const {currentUserAccess} = this.props
// if its a happy flow else it will be currentUserAccess is undefined
if(currentUserAccess){
this.props.usersListApi(currentUserAccess)
}
}
// inorder to resolve it
componentWillRecieveProps(nextProps){
const {currentUserAccess} = nextProps
if(currentUserAccess){
this.props.usersListApi(currentUserAccess)
}
}
const mapStateToProps = (state) => {
return {
currentUserAccess: state.access
}
}
This is the expected behavior of React Lifecycle events.
For componentWillMount
This function is called right before the component’s first render, so at first glance it appears to be a perfect place to put data fetching logic.
But, there’s a “gotcha,” though: An asynchronous call to fetch data will not return before the render happens. This means the component will render with empty data at least once. Which is why your dispatch function fails and you get undefined.
Instead you should use ComponentDidMountas the event in which you will fetch data.
By the time componentDidMount is called, the component has been rendered once.
In practice, componentDidMount is the best place to put calls to fetch data.
Using it makes it clear that data won’t be loaded until after the initial render. This reminds you to set up initial state properly, so you don’t end up with undefined state that causes errors.

redux state in componentWillRecieveProps issue with forward and back button

I have problem showing an alert dialog using redux. I want to show a success message after user has edited says a form. I set a flag through action > reducer > store.
Then I do
componentWillReceiveProps(nextProps) {
if(nextProps.data.updated_form) {
alert('form has been updated!');
}
}
There's problem in this approach. User updated the form, then he click somewhere to go to other route, then he click back to my form, that alert will trigger too.
The problem with your existing solution is that updated_form variable of the store doesn't change over time. So getting back to the page retriggers the alert. One simple solution would be to create another action creator to reset those changes on componentWillUnmount()`.
sample store reducer could like this
case 'RESET_CHANGES':
return {...state,updated_form:null}
And the react component will have similar code like this
componentWillUnmount() {
this.props.resetChanges() // assuming you have created action creator
}

How to model transient events in React/Redux?

While React with Redux is excellent at modeling UI state, there are occasionally situations where something just happens, the UI needs to handle that event in a discrete procedural way, and it doesn’t make sense to think of that transient event as a piece of state that would persist for any period of time.
Two examples, from a JS Bin-like code editor application:
A user exports their code to a GitHub gist. When the export is complete, we want to open a new browser window displaying the gist. Thus, the React component hierarchy needs to know the ID of the gist, but only at a single moment in time, at which point it will open the window and stop caring about the gist export altogether.
A user clicks on an error message, which causes the editor to bring the line where the error occurred into focus in the editor. Again, the UI only cares about which line needs to be focused for a single moment in time, at which point the (non-React-based) editor is told to focus the line, and the whole thing is forgotten about.
The least unsatisfying solution I’ve come up with is:
When the triggering event occurs, dispatch an action to update the Redux state with the needed information (gist ID, line to focus)
The React component that’s interested in that information will monitor the appropriate props in a lifecycle hook (componentWillReceiveProps etc.). When the information appears in its props, it takes the appropriate action (loads the gist window, focuses the editor on the line)
The component then immediately dispatches another event to the Redux store essentially saying, “I’ve handled this”. The transient event data is removed from Redux state.
Is there a better pattern for this sort of situation? I think one perhaps fundamental part of the picture is that the UI’s response to the action always breaks out of the React component structure—opening a new window, calling a method on the editor’s API, etc.
Your solution would certainly work, but these sorts of problems you bring up don't sound particularly well suited to be handled with redux at all. Just using plain React and passing the necessary functions to your component sounds a lot more natural to me.
For the export case, for instance, rather than dispatching an action which updates some state, which then triggers the new window to open, why not just open the new window in place of dispatching that action? If you have the info necessary to dispatch an action to trigger opening a window, you ought to be able to just open the window in the same place.
For the example where clicking an error message triggers calling a non-React, imperative api, pass a function from the nearest common parent of the error message and the editor. The parent can also maintain a ref to the wrapper around the editor. Even if it's multiple levels deep, it's not too bad to get a ref to what you want if you pass down a function to set the ref. Thus, the function passed down from the parent to the error message component can simply call a method on the ref it maintains to the editor. Basically, something like this:
class Parent extends Component {
constructor(...args) {
super(...args)
this.onErrorMessageClick = this.onErrorMessageClick.bind(this)
}
onErrorMessageClick(lineNumber) {
if (this.editor) {
this.editor.focusOnLine(lineNumber)
}
}
render() {
return (
<div>
<ErrorMessage onClick={ this.onErrorMessageClick } lineNumber={ 1 } />
<ErrorMessage onClick={ this.onErrorMessageClick } lineNumber={ 2 } />
<EditorWrapper editorRef={ (editor) => { this.editor = editor } } />
</div>
)
}
}
const ErrorMessage = ({ onClick, lineNumber }) => (
<button onClick={ () => onClick(lineNumber) }>
{ `Error Message For ${lineNumber}` }
</button>
)
// Just adding EditorWrapper because your editor and error messages probably aren't right next to each other
const EditorWrapper = ({ editorRef }) => <Editor ref={ editorRef } />
class Editor extends Component {
focusOnLine(lineNumber) {
// Tell editor to focus on the lineNumber
}
render() {
...
}
}
I often use redux-thunk for these types of events.
Essentially, its not different to setting up a normal thunk, only I don't dispatch an action in it.
const openGist = (id) => () => {
// code to open gist for `id`
}
I then use this action creator like I would any other from the component triggering it, e.g. mapped in mapDispatchToProps and called in an onClick handler.
A common question I get asked is why don't I just put this code straight into the component itself, and the answer is simple - testability. It is far easier to test the the component if it doesn't have events that cause side effects and it's easier to test code that cause side effects in isolation to anything else.
The other advantage is that more often than not, the UX designers will step in at some point and want some kind of feedback to the user for some of the events of this nature (e.g. briefly highlight the error row) so adding an X_COMPLETED action to the event is much easier at this point.

Resources