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

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

Related

React Hook - Cannot Call Parent Component Dispatch in Child Component onComplete Callback

I've tried several solutions to fix this problem, and none have worked.
I have a third-party library that calls an asynchronous function to load something to render in the DOM.
I have a component that wraps the library for the DOM piece and when it finishes I want to be able to call an onComplete method with it.
Everything works fine. However when I call the onComplete function, and then inside that function is a call to dispatch something to a context it blows up and gives me the
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
Code
export function ParentComponent(props: any) {
const dispatch: any = React.useContext(dispatchContext);
const onComplete = React.useCallback(
(data: any) => {
if (dispatch !== null && dispatch !== undefined) {
dispatch({
payload: { new_data: "thing" },
type: dispatchType.UPDATING
});
}
},
[dispatch]
);
return (
<div>
<ChildComponent onComplete={onComplete} />
</div>
);
}
interface IProp {
onComplete?: function;
}
export function ChildComponent(props: IProp) {
React.useEffect(() => {
if (library.is_ready) {
library.load(data).then(
() => {
console.log("Success");
props.onComplete(library);
},
(error: any) => {
console.error("ERROR");
}
);
}
}, [library]);
return <div id="library-dom-element"></div>;
}
If I comment out the dispatch function this won't blow up.
It blows up when the asynchronous function is completed and calls the onComplete function which calls the dispatch
ChildComponent.libary.is_ready → ChildComponent.libary.load → ChildComponent.libary.onComplete → ParentComponent.onComplete → dispatch
I have tried adding useCallback to everything. I have tried using useRef to track an is_mounted variable to determine if the DOM pieces are loading correctly.
I just cannot figure out how to call dispatch on the completion of the third party library.
That's literally it.
Can anyone help or provide guidance on this? I thought I understood how these pieces work together, but I'm missing something.
The application I've been making has been pretty complex.
The bug I provided here is a result of component initialization order that arises because of how I am toggling state.
When I created a delay function to help buffer initialization this resolved the issue for the time being.
Apologies for this question as the design pattern does appear to work, there just needs to be accounting for how your React application's complexity is being managed.
Thank you to everyone who took a look :) <3

Why ErrorBoundary needed in React.js?

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>

Call api before first render in functional component in React.js

If I want to call API after the first rendering of component, I know we have useEffect hook to call the API method. (I am talking about functional components only. No class component).
Is there any way, I can call the API before my component renders the first time.
The reason for this question is, If some UI part is dependent on API, I do not want to show any incomplete information to the user on the first render also, which will be changed once I get the data from API.
This seems to be a bad experience with UI.
Edit: I got a couple of advice to use useLayoutEffect or any consumable flag to check if it is rendered or not. I have checked useLayoutEffect does not work, and by using the consumable flag, we are increasing the complexity only.
Do we have any better way for this?
I think useLayoutEffect can be used for something like this, by passing in an empty array as second argument. useLayoutEffect(() => {...}, []);
Updates scheduled inside useLayoutEffect will be flushed synchronously, before the browser has a chance to paint.
Although you can always fetch the data in the parent component and pass it as props. Or - if you don't mind it being an experimental feature for now - React Suspense is trying to solve this exact problem.
There are no correct ways to make API call before component rendered from the same component.
You may preferred make API call in parent component and render presentation component when and only when have consumable data.
Another workaround for such case is keep consumable flag inside component, make request inside useEffect, render nothing or some kind loader and render something only when request completed with success.
on calling api it is not responding exact on its first render but giving exact response when it's being hit second time
You can have a spinner or loading component be rendered first conditionally (isLoading for example):
if(isLoading) return <Spinner />
and have the api call set (isLoading) to false on status 200 for example.
Just came across something, which may help someone in future. So we can use some library but the specific one I would mention here is React Query
React query does exactly what we are trying to achieve, the hooks like useQuery fetch data as soon as rendering starts so you don’t have to wait until react loads the entire component as follows
// with react query
const { status, data, error, isFetching } = useQuery(
['data'],
async () => {
const data = await (
await fetch(`${API_BASE_URL}/data`)
).json()
return data
}
)
// without react query
useEffect(() => {
try {
setLoading(true)(async () => {
const data = await (await fetch(`${API_BASE_URL}/data`)).json();
setData(data);
})();
} catch (error) {
setError(error);
} finally {
setLoading(false);
}
}, []);
Here is the article link if you want to read

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.

Categories

Resources