This content originally appeared on Bits and Pieces - Medium and was authored by Lakindu Hewawasam
Improve Resiliency When Designing & Developing Web Components.
Almost all modern-day web applications are bound to run into errors at some point during their lifetime.
Unfortunately, it is never “fail-safe,” as your users can run into an error depending on how they use your application.
For instance, two users may execute the same feature differently, causing one user’s action to fail while the other to succeed.
Therefore, in such cases, it is necessary to implement proper error handling in the front end to ensure that your application provides a desirable user experience to its customers while remaining available and resilient.
However, developers tend to overthink when handling errors in a front-end application, for there are several options available to handle an error.
Hence, it tends to create an indecisive process and can sometimes lead to ineffective implementations of error handling. Therefore, this article covers best practices that all developers can use to handle errors in their web applications.
What Causes Errors in Web Components?
Generally, the likelihood of an application having a breaking error in production is minimal. However, there are cases where this might happen due to a rush to release or simply missing out on a test case.
But, specific instances cause “edge cases” to occur that can create errors. Simply put, an “edge case” is a particular error that a developer can miss and is hard to reproduce again.
These edge cases are out of the developers’ hands as these are very hard to spot but can often lead to the failure of mission-critical applications.
And, more often than not, these edge cases create runtime or rendering errors in the front end that can cause the entire application to crash. This causes the user to load the application from scratch.
Figure — Client-side exception on front-end applications.
The figure shows a client-side exception triggered due to an error in a component. As depicted above, the entire application stops and forces users to reload the site to resume their work.
Therefore, developers need to ensure that such errors are handled and that the application can work without having any negative impacts because of an error.
Best Practises in Error Handling
Developers can implement a few best practices to handle errors in their web components. However, these implementations differ based on the framework.
For example, one best practice — “Error Boundary,” is implemented differently across frameworks such as React and Angular even though they follow the same theory.
Therefore, it’s essential to understand that the idea behind these practices remains the same whilst their implementations may differ based on the framework or language in which the web component is written.
In this article, I will be implementing these best practices using React. However, you can implement these concepts using your preferred web component framework.
Using Try-Catch Blocks
If you’re familiar with error handling in the backend, you may have come across the use of try-catch blocks. A simple try-catch block is depicted below.
try {
// business operation
} catch(err){
// in case of error
console.log(err)
}
These try-catch blocks are effective when catching errors thrown with imperative code — for example, catching errors in a callback function or even inside an effect.
However, it is not a suitable approach for catching errors for a React component as this does not work for declarative code written in JSX.
The snippet shown below highlights the use of a try-catch block.
const onMenuClick = () => {
try {
console.log("Menu clicked");
throw new Error("Menu clicked");
console.log("Menu clicked");
} catch (err) {
console.error(err);
}
};
The snippet above depicts the use of the try-catch block to handle errors thrown in a callback function.
Typically, when an error is thrown in a callback function, it causes a runtime exception that can cause the client-side application to crash, halting all application interactions.
However, with the help of the try-catch block, the code execution will immediately jump to the catch block in case of an error and handle errors accordingly.
Generally, you could display an error message to the user indicating that the system has encountered an error.
Using Optional Checks
You might have run into an error where the data model you try to access in your JSX component needs to be defined.
For example, this type of error is often created when you try to access a child nested data attribute, as shown below.
const UserCard = () => {
const [user, setUser] = useState(undefined);
// will throw an error since user is undefined
return <div>{user.name}</div>;
};
The snippet shown above illustrates a React component UserCard, that renders information for a user.
However, the above snippet does not initialize a user (remains undefined). When the state object is accessed while it's undefined, the component will throw an error and crash the application.
Hence, a developer must check for the data’s availability. This can be done in two ways:
- Conditional rendering
- Optional checks
With conditional rendering, a developer can render the information only if the information is present.
For example, this is shown in the code snippet below:
const UserCard = () => {
const [user, setUser] = useState(undefined);
if (!user) {
return <div>Loading...</div>;
}
return <div>{user.name}</div>;
};
The snippet shown above will render the user information only if the user object has a value.
On the other hand, developers can use a more efficient approach known as optional checks via the ? notation in JavaScript.
This will ensure that JavaScript only accesses the sub-value if the object exists. Its implementation is shown below.
const UserCard = () => {
const [user, setUser] = useState(undefined);
return <div>{user?.name || 'Not Available'}</div>;
};
The snippet depicted above renders the name only if the user object has a value.
Using Error Boundaries
Lastly, the concept of an “error boundary” is very helpful in catching errors within an application.
While the first two practices focused on handling errors within a component, an error boundary can help catch errors across a set of components.
Simply put, an error boundary is a component that can catch JavaScript errors anywhere in its child component tree, log them and display a fallback UI rather than showing a crashed UI.
This makes error boundaries the perfect candidate for catching errors in declarative code but a poor candidate for imperative code.
Therefore, an error boundary will not catch errors thrown within:
- An event handler
- An asynchronous code
- Server-side rendering (since it is used on the client side)
- Errors thrown in the boundary (catches only errors in its children)
This ensures that your application has a common component to process errors (and can ideally be placed at the top of your application, i.e., in the App component, ensuring that an error boundary covers your entire application).
It will also ensure that your application works without an error when a component tries to render undefined data.
You can use the snippet below to implement an error boundary in React.
class MyCustomErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// update the error state to show the error UI
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// do additional error handling logic here
console.log(error, errorInfo);
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return <p>Error.Rendering Fallback UI for Error.</p>;
}
return this.props.children;
}
}
Please note that the error boundary must be a class component in React.
Hereafter, you can place your error boundary in the App component, as shown below.
<MyCustomErrorBoundary>
{children}
</MyCustomErrorBoundary>
This will ensure that all the components mounted in your application will use the error boundary to handle its errors in the declarative code.
Final Thoughts
Errors are not something that you can avoid. Almost every application has its share of client-side errors that can sometimes cause an undesirable user experience.
However, this does not mean that there is nothing that you can do about it.
Adopting the best practices discussed in this article will ensure that your web components become resilient enough to withstand errors and keep your application available to your users anytime.
I hope that you have found this article helpful.
Thank you for reading.
Build apps with reusable components like Lego
Bit’s open-source tool help 250,000+ devs to build apps with components.
Turn any UI, feature, or page into a reusable component — and share it across your applications. It’s easier to collaborate and build faster.
Split apps into components to make app development easier, and enjoy the best experience for the workflows you want:
→ Monorepo
- How We Build Micro Frontends
- How we Build a Component Design System
- Bit - Component driven development
- 5 Ways to Build a React Monorepo
- How to Create a Composable React App with Bit
Best Practices in Handling Errors in Web Components was originally published in Bits and Pieces on Medium, where people are continuing the conversation by highlighting and responding to this story.
This content originally appeared on Bits and Pieces - Medium and was authored by Lakindu Hewawasam
Lakindu Hewawasam | Sciencx (2023-01-27T07:22:49+00:00) Best Practices in Handling Errors in Web Components. Retrieved from https://www.scien.cx/2023/01/27/best-practices-in-handling-errors-in-web-components/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.