A clean way to handle loading and error state in React application.

Lokesh kumar Jain
CodeX
Published in
3 min readApr 19, 2021
React state management. clean sate mess, loading and error handling in react
Photo by Unsplash

User-facing applications deal with different states of the data. There are many ways to show the UI based on the states of the applications. Here we will be taking an example of the Application which is mainly based on HTTP requests.

Most of the react front-end application is mainly doing HTTP request and shows the response to the user. For a better UX, our UI needs to communicate user about the whole process.

Let’s write it in the product requirement statement. Fetch users list and show the user. Meanwhile, data is being fetched, show Loader/message. When the request completes show the response of Error.

export default function DisplayUsers() {
// Keeping code short with one API response
const [state, setState] = useState({
loading: true,
response: null,
error: null
});
useEffect(() => {
// make http request
fetch('https://jsonplaceholder.typicode.com/users')
.then < IUser[] > (res => res.json())
.then(response => setState({
loading: false,
response,
error: null
}))
.catch(error => setState({
loading: false,
response: null,
error
}));
}, []);
return <div>
{state.error ? 'Error' : null}
{state.loading
? <div>Loading...</div>
: <div>Display users:
<div>
we have total users : {state.response.length}
{state.response.map(user => <User user={user} />)}
</div>
</div>
}
</div>
}

As the code grows or other conditions come e.g. if the user list is empty then show the custom message. It becomes to navigate in the JSX template.

Step one to clean this mess to separate the state blocks. Having then will make it easy to navigate the state blocks and we will be able to argue about the code.

export default function DisplayUsers() {
const [state, setState] = useState({
loading: true,
response: null,
error: null
});
useEffect(() => {
// make http request
fetch('https://jsonplaceholder.typicode.com/users')
.then < IUser[] > (res => res.json())
.then(response => setState({
loading: false,
response,
error: null
})).catch(error => setState({
loading: false,
response: null,
error
}));
}, []);
if (state.error) {
// This is Error block. Do everything related to error hangling.
return <div style={{ color: 'red' }}> Error state/ every thing related to Error</div>
}
if (state.loading) {
// Loading state block. Do everything related to loading show loader/ animation.
return <div>Loading...</div>
}
// Better and clean code with less ternary conditions.
return <div>
<h3>Display users:</h3>
we have total users : {state.response.length}{state.response.map(user => <User user={user} />)}
</div>
}

With these state blocks, we have more control over the related state. Now we have better building blocks.

This is done for a single component, but for a big application, there will be many more state-based components.

There will be a lot of repetition. This can be solved and made short with this generic component.

// StateHandlerComponent.js to solve this repetition.

//This component will deal with state blocks 
const StateHandler = (props) => {
const {
showLoader = false,
showError = false,
showEmpty = false,
retryHandler = () => { console.log("Retry!!") } } = props;
if (showLoader) {
return <Loader fullScreen={false} />;
}
if (showError) {
return <InfoComponent icon={<ErrorIcon />} buttonClickHandler={retryHandler} />
}
if (showEmpty) {
return <div class={style.exceptionContainer}>
<Empty message='No data available' />
</div>;
}
return props.children;
}
export default StateHandler;

Final output with the use of this StateHandler component.

export default function DisplayUsers() {
const [state, setState] = useState({
loading: true,
response: null,
error: null
});
useEffect(() => {
// make http request
fetch('https://jsonplaceholder.typicode.com/users')
.then < IUser[] > (res => res.json())
.then(response => setState({
loading: false,
response,
error: null
})).catch(error => setState({
loading: false,
response: null,
error
}));
}, []);
//This component deals will only the user list display
return <StateHandler showError={state.error} showLoader={state.loading} showEmpty={state.response.length == 0}>
<div><h3>Display users:</h3>
we have total users : {state.response.length}
{state.response.map(user => <User user={user} />)}</div>
</StateHandler>;
}

Now, State handling is done by `StateHandler`component. Make it clean and easy to reason.

I will be sharing more tips soon. keep learning and let me know if this component helped you to clean some state mess in your codebase :)

--

--

Lokesh kumar Jain
CodeX
Writer for

Love Web building technologies working as frontend team lead loves working on JavaScript tech stack, as Reactjs, Angular, HTML5