Why ErrorBoundary needed in React.js? - javascript

What kind of errors does it handle, and is there any real-world scenario?
I read the docs but it doesn't give a good example.
And docs also mention it handles UI errors, but UI errors can be solved at development time, so exactly why do we need "Error Boundaries".

Error Boundaries are like try-catch blocks for react components . They allow you to catch and handle unexpected errors in react components gracefully, for example, display a message to the user that something went wrong.
These errors, if not handled will crash your app. Error boundaries allow you to catch those errors and handle them in a way that prevents your app from crashing and instead provide a user friendly message to the user indicating that something went wrong.
Keep in mind that Error Boundaries do not handle errors for:
Event handlers
Asynchronous code (e.g. setTimeout or requestAnimationFrame callbacks)
Server side rendering
Errors thrown in the error boundary itself (rather than its children)
Here's a nice article that explains error boundaries

but UI errors can be solved at development time
UI errors can't be solved at development time, although typing like Flow & Typescript indeed help a lot, still, there may be run-time errors that usually come for the data server.
Error Boundaries used for catching run time errors and displaying a friendly UI.
See Compile time vs Run time errors.
ErrorBoundary is a class components that implements getDerivedStateFromError and componentDidCatch in order to add additional render on fall back UI.
Currently, it can only be implemented with a class component, typically its the only class component in your application, the rest is function components with hooks.
In real-life use cases, you wrap your application with some <ErrorBoundary/> component and render the desired UI.
class ErrorBoundary extends React.Component {
state = {
hasError: false,
error: { message: '', stack: '' },
info: { componentStack: '' }
};
static getDerivedStateFromError = error => {
return { hasError: true };
};
componentDidCatch = (error, info) => {
this.setState({ error, info });
};
render() {
const { hasError, error, info } = this.state;
const { children } = this.props;
return hasError ? <ErrorComponent/> : children;
}
}
<ErrorBoundary>
<App/>
</ErrorBoundary>

Related

How to imperatively handle an error, with react-query?

Being that react-query is heavily based on the declarative approach, the error handling I see in all examples looks something like this:
function Todos() {
const { isLoading, isError, data, error } = useQuery('todos', fetchTodoList)
if (isLoading) {
return <span>Loading...</span>
}
if (isError) {//If there is an error, render different JSX
return <span>Error: {error.message}</span>
}
return (
<ul>
{data.map(todo => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
)
}
But what if I want to just show an alert, in case of an error? Or perhaps, i'm using some error handling interface, that has an imperative trigger? Something like this:
if (isError) alert(`An error has occurred: ${error.message}`)
In this case, I get two alerts. Something causes the component to re-render, which of course leads to "duplicate" error handling.
Why is this important to me? Because my error handling logic might not be based on rendering some specific JSX within my component, but rather on some manual one-time trigger. The alert is just a basic example.
Any suggestions will be greatly appreciated!
Have you tried using the onError callback?
It looks like this :
const useTodos = () =>
useQuery(['todos'], fetchTodos, {
// ⚠️ looks good, but is maybe _not_ what you want
onError: (error) =>
toast.error(`Something went wrong: ${error.message}`),
})
You see, the onError callback on useQuery is called for every Observer, which means if you call useTodos twice in your application, you will get two error toasts, even though only one network request fails

What benefits does React suspense have?

I know React suspense is new and hasn't been officially released for production yet but I was wondering what the main benefits of it are/or using it would be?
I can't see anything it does as being "new" or replacing something that doesn't already exist?
I know it allows me to load stuff from top to bottom but I can do that anyway in react using my own components
can anyone give me some ideas as to what it may be good for?
it has several benefits to use:
it makes code splitting easy
(with new usecase) it makes data fetching so easy! Read this
it just suspends your component rendering and renders a fallback component until your component makes itself ready to show, by that you can create a skeleton flow for your async components so easily event with a simple UI ( imagine instead of created a loading login by useState Api or something else )
these were just simple benefits of Reacts Suspense/lazy api.
First of all, I would like to mention that Suspense is officially released since React 16.6. It is production-ready and it is not limited only to code-splitting. Any asynchronous code can be integrated with it.
As of the benefits, consider the following use-case:
We have several components that all use some asynchronous code inside them (like fetching remote resources)
We need to display a loading indicator until all components are finished doing their job
We need to display an appropriate error if some of the components have failed to do their duty
Old way
The good old way of doing this would be:
Create a wrapper component for showing loading indicator and error messages
Keep track of loading and error state inside of each component and inform the wrapper component of state changes
Does this all look like unnecessary, hard to change boilerplate? Yes, it does).
New way
React introduced the Suspense component and Error Boundaries to eliminate this boilerplate and to declaratively describe the desired behavior.
Check this out:
<Exception fallback="An error has occured">
<Suspense fallback="Loading...">
<OurComponent1 />
<OurComponent2 />
<OurComponent3 />
</Suspense>
</Exception>
Example
Suppose we want to fetch users' data from the remote resource.
const fetchUsers = async () => {
const response = await fetch("https://jsonplaceholder.typicode.com/users");
const users = await response.json();
console.log("Users data", users);
return users;
};
I will use makeSuspendableHook to integrate our asynchronous fetch within <Suspense> and Error boundary.
const useUsers = makeSuspendableHook(fetchUsers());
In our component, all we should care about is the actual data and its representation.
const Users = () => {
const users = useUsers();
return (
<div>
List fetched users:
<ul>
{users.map(({ name }) => (
<li>{name}</li>
))}
</ul>
</div>
);
}
Finally, I will use Exception as an Error Boundary implementation to stitch everything together.
export default () => (
<Exception fallback="An error has occurred">
<Suspense fallback="Waiting...">
<Users />
</Suspense>
</Exception>
);
Play with web example at codesandbox.io
Play with native example at snack.expo.io

How to check if a JSON object is present in a file in JavaScript

I am building an app in react-native and have been trouble getting some trouble doing error checking. I am using redux and thunk to store a JSON file from the API that I am searching from. Sometimes, the search will lead back with a JSON file that contains an error message(most likely due to spelling error or unsure about what I am exactly searching for). I am trying to build a function in my app that will run in an componentWillMount function that first checks if the file has an error meassage and if so will send it back to the home screen to redo the search. The problem that am encountering is that I do not know what to code inorder to see what the error is or even if there is an error to begin with.
this is what the error object will look like in the JSON file and this
"error": {
"type": "NameResolutionException",
"message": "Name resolution error: Taxon 'Mallard' or 'Bat' resolve to multiple nodes"
},
This is the function that I built
componentDidMount = () => {
console.log("STATE", this.state);
if(this.props.response.articles.error == undefined){
if(this.props.response.articles.error.type === "NameResolutionException"){
Alert.alert("Please Refine Search or Specify Taxon.")
.then(()=>this.props.navigation.navigate("Home"));
}
}
};
The if statements will never hit despite the fact that the json file will the error object in it.
The expected output is that the error will be caught and then the app will go back to its the home screen but instead the app will fail because certain components will be missing.
Your code seems not correct, you are saying if error is undefined then comparing for a string value. This is contradictory.
It should be something like this, considering error is an object not an array
componentDidMount = () => {
console.log("STATE", this.state);
if(this.props.response.articles.error && this.props.response.articles.error.type){ // considering error object may or may not be present
if(this.props.response.articles.error.type === "NameResolutionException"){
Alert.alert("Please Refine Search or Specify Taxon.")
.then(()=>this.props.navigation.navigate("Home"));
}
else if (this.props.response.articles.error.type === "OtherPossibleStringException"){
}
else {
//Any unhandled error case
}
}
};

How to let react electron ignore undefined error?

React electron on windows, if A is null, call A.test will make the application stop working, then the user has to close the application and restart it.
How to let react ignore the error, and continue work. The code has many A.test, I can't write everywhere if(A) A.test.
If this can't be resolved, can I print the error on the web view? So I don't have to remote visit the user's computer to see the console error.
NOTE
I think the solution is to use react error boundaries, as suggested in the console.
You already pointed out that you're using error boundaries, so after testing your scenarios in this fiddle I believe your implementation might be incorrect.
Given a similar implementation for ErrorBoundary in the docs:
class ErrorBoundary extends React.Component {
state = { hasError: '' };
render() {
return this.state.hasError ? (
<span>Oops! Something went wrong:<br />{this.state.hasError}</span>
) : this.props.children;
}
}
ErrorBoundary.getDerivedStateFromError = (error) => ({ hasError: error.toString() });
This component will render the fallback when any of its children breaks.
Error boundaries are React components that catch JavaScript errors anywhere in their child component tree, log those errors, and display a fallback UI
It will look similar to:
<MyReactApp>
<ErrorBoundary>
<ChatContent />
</ErrorBoundary>
</MyReactApp>
Now any error in ChatContent will be catch by ErrorBoundary giving you the opportunity to render the fallback like:
Oops! Something went wrong:
ReferenceError: test is not defined
The code has many A.test, I can't write every where if(A) A.test
But why? You can use some editor for multi file editing.
So you can replace A.test() to safeTest(A) function.
export const safeTest = (Obj) => {
if (Obj) {
Obj.test();
} else {
// Any action you want
}
}
It is hard to offer an answer to your question because I don't see your project codes, but if your react version is 16 you can use a special component lifecycle method that name is componentDidCatch.
Inside this method you will have these values:
componentDidCatch(error, info) {
// Do something with error and info
}
Even you can use setState inside this method and show you want. I think this method can help you for your second desire, the printing error in web view.
I tend to favor using default props. You can set a value for the component to assign to a prop if the prop is passed in undefined. For example, if your component depends on an array nested within an object, you could set that value as an empty array by default. This is especially handy when your component depends on an array of results from an API call, but the component renders before the request finishes.
If you want to make the minimal effort to catch all the unhandled errors from both main and renderer processes within Electron as well as showing them to the user via a dialog, the easy way is to use electron-unhandled which does exactly that:
After having installed it (npm i electron-unhandled), in both your main and renderer entry files (likely their root index.js), you just have to add, at the beginning:
const unhandled = require('electron-unhandled');
unhandled({ showDialog: true });
Now, that being said, it's a good practice to use a global error catcher but it's a really bad one if you use only that. You should try covering your error handling more accurately, at least method by method:
.then() { ... }.catch(err => ...) for your promises,
(..., (err, res) => { if (err !== null) { ... } ... ) for your callbacks,
try { ... } catch(err) { ... } for non-async or await-based code code parts.
And, as a side-note, I myself created a dependenciless library to make it safe and easy to create a global errors dictionary in order to well-organize your errors but there are other alternatives if this one doesn't fit your needs.
I guess the best possible solution to this would be surrounding your A.test in try and catch. In this case what you can do is catch the error is A is null and perform some error page from your side incase you want it or just keep the error silent incase you dont want to perform any operation and suppress the error and continue execution.
You can also wrap A.test in a function with try-catch and use that function instead of A.test. In this way you can avoid multiple try-catch block and you can handle the error as per your requirement here.

Architecture in a react native app using WebSockets

I have a React Native app I'm going to be building that uses WebSockets. I have a WebSocket library written in JavaScript and I'm simply re-using it for this project, which is fantastic.
My question is, being new to React/React Native, what is the best practice for setting up and maintaining all of the traffic going through the WebSocket?
Initially my idea was to create the websocket in the main App component, something like this:
export default class App extends Component {
constructor(props) {
super(props);
this.ws = new WebSocket;
}
componentWillMount() {
console.log(this.ws);
}
render() {
console.log("We are rendering the App component.....");
return (
<View style={styles.container}>
<Text style={styles.welcome}>Hello, world</Text>
</View>
);
}
}
The actual WebSocket class would contain all of the respective connection handling:
ws.onopen = () => {
// connection opened
ws.send('something'); // send a message
};
ws.onmessage = (e) => {
// a message was received
console.log(e.data);
};
ws.onerror = (e) => {
// an error occurred
console.log(e.message);
};
ws.onclose = (e) => {
// connection closed
console.log(e.code, e.reason);
};
My question is, since the data coming through WebSocket will be applicable for state through many components in the React Native app, but it is not a class that will extend React.Component, do I not interact with Redux in the WebSocket class? Do I move all of the WebSocket connection handling to the App component and dispatch actions there to Redux?
What's the common pattern here to instantiate my WebSocket class and ensure that all traffic in it is properly getting passed to Redux so all component's state will funnel correctly?
Great answers here so far. Just wanted to add that where you keep your data should really be a decision based on what type of data it is. James Nelson has an excellent article on this topic that I refer to regularly.
For your case, let's talk about the first 3 types of state:
Data
Communication State
Control State
Data
Your WebSocket connection is generic and could technically return anything, but it's likely that the messages you're receiving are data. For example, let's say you're building a chat app. Then, the log of all messages that have been sent and received would be the data. You should store this data in redux with a messages reducer:
export default function messages(state = [], action) {
switch (action.type) {
case 'SEND_MESSAGE':
case 'RECEIVE_MESSAGE': {
return [ ...state, action.message ];
}
default: return state;
}
}
We don't have to (and we shouldn't) have any WebSocket logic in our reducers, as they are generic and don't care where the data is coming from.
Also, note that this reducer is able to handle sending and receiving in exactly the same way. This is because the network communication is handled separately by our communication state reducer.
Communication State
Since you're using WebSockets, the types of communication state you want to track may differ from my example. In an app that uses a standard API, I would track when a request is loading, failed, or successful.
In our chat app example, you'll probably want to track these request states whenever you send a message, but there could be other things you want to classify as communication state as well.
Our network reducer can use the same actions as the messages reducer:
export default function network(state = {}, action) {
switch (action.type) {
case 'SEND_MESSAGE': {
// I'm using Id as a placeholder here. You'll want some way
// to tie your requests with success/failure receipt.
return {
...state,
[action.id]: { loading: true }
};
} case 'SEND_MESSAGE_SUCCESS': {
return {
...state,
[action.id]: { loading: false, success: true }
};
} case 'SEND_MESSAGE_FAILURE': {
return {
...state,
[action.id]: { loading: false, success: false }
};
}
default: return state;
}
}
This way, we can easily find the status of our requests, and we don't have to bother with loading/success/failure in our components.
However, you might not care about the success/failure of any given request since you're using WebSockets. In that case, your communication state might just be whether or not your socket is connected. If that sounds better to you, then just write a connection reducer that responds to actions on open/close.
Control State
We'll also need something to initiate the sending of messages. In the chat app example, this is probably a submit button that sends whatever text is in an input field. I won't demonstrate the whole component, as we'll use a controlled component.
The takeaway here is that the control state is the message before it's sent. The interesting bit of code in our case is what to do in handleSubmit:
class ChatForm extends Component {
// ...
handleSubmit() {
this.props.sendMessage(this.state.message);
// also clear the form input
}
// ...
}
const mapDispatchToProps = (dispatch) => ({
// here, the `sendMessage` that we're dispatching comes
// from our chat actions. We'll get to that next.
sendMessage: (message) => dispatch(sendMessage(message))
});
export default connect(null, mapDispatchToProps)(ChatForm);
So, that addresses where all of our state goes. We've created a generic app that could use actions to call fetch for a standard API, get data from a database, or any number of other sources. In your case, you want to use WebSockets. So, that logic should live in your actions.
Actions
Here, you'll create all of your handlers: onOpen, onMessage, onError, etc. These can still be fairly generic, as you've already got your WebSocket utility set up separately.
function onMessage(e) {
return dispatch => {
// you may want to use an action creator function
// instead of creating the object inline here
dispatch({
type: 'RECEIVE_MESSAGE',
message: e.data
});
};
}
I'm using thunk for the async action here. For this particular example, that might not be necessary, but you'll probably have cases where you want to send a message then handle success/failure and dispatch multiple actions to your reducers from within a single sendMessage action. Thunk is great for this case.
Wiring It All Together
Finally, we have everything set up. All we have to do now is initialize the WebSocket and set up the appropriate listeners. I like the pattern Vladimir suggested--setting up the socket in a constructor--but I would parameterize your callbacks so that you can hand in your actions. Then your WebSocket class can set up all the listeners.
By making the WebSocket class a singleton, you're able to send messages from inside your actions without needing to manage references to the active socket. You'll also avoid polluting the global namespace.
By using the singleton set up, whenever you call new WebSocket() for the first time, your connection will be established. So, if you need the connection to be opened as soon as the app starts, I would set it up in componentDidMount of App. If a lazy connection is okay, then you can just wait until your component tries to send a message. The action will create a new WebSocket and the connection will be established.
You can create dedicated class for WebSocket and use it everywhere. It's simple, concise and clear approach. Moreover you will have all stuff related to websockets encapsulated in one place! If you wish you can even create singleton out of this class, but the general idea is this:
class WS {
static init() {
this.ws = new WebSocket('ws://localhost:5432/wss1');
}
static onMessage(handler) {
this.ws.addEventListener('message', handler);
}
static sendMessage(message) {
// You can have some transformers here.
// Object to JSON or something else...
this.ws.send(message);
}
}
You have only run init somewhere in index.js or app.js:
WS.init();
And now you can loosely send message from any application layer, from any component, from any place:
WS.sendMessage('My message into WebSocket.');
And receive data back from WebSocket:
WS.onMessage((data) => {
console.log('GOT', data);
// or something else or use redux
dispatch({type: 'MyType', payload: data});
});
So you can use it everywhere even in redux in any action or somewhere else!
There are no official guidelines about that. I think using a component is confusing because it will not be rendered, and I guess if you use Redux you want to share the data from websocket anywhere in the application.
You can give the dispatch function to your Websocket manager.
const store = createStore(reducer);
const ws = new WebSocketManager(store.dispatch, store.getState);
And use this.dispatch inside your class methods.
// inside WebSocketManager class
constructor(dispatch, getState) {
this.dispatch = dispatch;
this.getState = getState;
}
You can also use middlewares to handle side effects, I think it is the recommended way. There are two great libraries that you can look :
redux-saga
redux-observable

Categories

Resources