I'm very new to react I'm experimenting with the use-effect hook
I'm trying to call and async function inside use effect
something like this :
useEffect(() => {
async function fetchData() {
await new Promise(resolve => setTimeout(resolve, 1000));
console.log("done");
throw new error("error in here");
}
try {
fetchData();
}
catch(error){
console.log("error catching",error);
}
});
is this code the catch will not work since useEffect will return before fetchData
i'm aware that adding fetchData().catch(error => {console.log("error catched")});
will resolve the problem.
is this a correct solution or can i do better ?
The React useEffect hook does not support async/await. So you can't use a traditional try/catch as this is only supported for async/await style syntax. You'll need to use it like this:
useEffect(() => {
async function fetchData() {
await new Promise(resolve => setTimeout(resolve, 1000));
console.log("done");
throw new error("error in here");
}
fetchData().catch(err => {
console.log("error catching",error);
});
});
Related
The Abortcontroller signal is not working for me with Axios in React.
I wanted to replace CancelToken (as it's deprecated) with the AbortController, but it is not working, respectively the requests are not being canceled.
let testController: AbortController;
function loadTest() {
testController = new AbortController();
TestAPI.getTest(testController.signal)
.then((e) => {
console.log(e.data);
})
.catch((e) => {
console.error(e);
});
}
Also in the UseEffect Cleanup I do this (here it should cancel) and also the signal's state is set to aborted, but still the request is not canceled:
useEffect(() => () => {
if (testController) testController.abort();
// console.log(testController.signal.aborted) => **true**
}, []);
Here is my API, where I pass the AbortSignal to the request:
getTest(signal?: AbortSignal): Promise<AxiosResponse<Test[]>> {
return axios.get(`${URI}/test`, { signal });
},
When using Axios.CancelToken.source was working fine, but now with the AbortController, the request is never canceled.
Using: "axios": "^0.26.0",
Did someone manage to integrate the AbortController with React and Axios? Or does the AbortController only work with fetch?
The axios.CancelToken API isn't deprecated as far as I can tell, it's still in the spec, but according to the docs axios also supports the AbortController of the fetch API.
Cancellation
Axios supports AbortController to abort requests in fetch API way:
const controller = new AbortController();
axios.get('/foo/bar', {
signal: controller.signal
}).then(function(response) {
//...
});
// cancel the request
controller.abort()
It's not clear exactly where testController is declared:
let testController: AbortController;
but I suspect it's in the body of the function component and redeclared on a subsequent render cycle.
I suggest using a React ref to store an AbortController, and reference this ref value around your app. This is so the component holds on to a stable reference of the controller from render cycle to render cycle, to be referenced in any useEffect hook cleanup function to cancel in-flight requests if/when the component unmounts.
const abortControllerRef = useRef<AbortController>(new AbortController());
function loadTest() {
TestAPI.getTest(abortControllerRef.current.signal)
.then((e) => {
console.log(e.data);
})
.catch((e) => {
console.error(e);
});
}
useEffect(() => {
const controller = abortControllerRef.current;
return () => {
controller.abort();
};
}, []);
I would recommend to read this post.
In a nutshell you would like to use useEffect to create controller, and, what is more important, to use return statement to abort the controller.
useEffect(() => {
const controller = new AbortController();
const signal = controller.signal;
getData(signal)
//cleanup function
return () => {controller.abort();};
}, [fetchClick]);
getData function can then be your axious call in the form:
const getData = async (signal) =>{
const res = await axios.get(url, {signal: signal}).then(...)
}
Abort controller often use in useEffect to fetch some data. So, in order to implement the control you can try this:
//...
const [data, setData] = useState([]);
useEffect(() => {
const controller = new AbortController();
axios
.get("https://somedata.com", { signal: controller.signal })
.then(res => {
setData(res.data);
})
.catch(err => console.log(err));
// return cleanup function to abort request
return () => {
controller.abort();
};
}, []);
//...
There's my code example, hope this helps:
useEffect(() => {
const abortController = new AbortController();
const getData = async () => {
try {
const res = await axios("/api/data/", {
signal: abortController.signal,
});
const data = res.data
} catch (error) {
if (error.name !== "CanceledError") {
/* Logic for non-aborted error handling goes here. */
console.log('error:', error)
}
}
};
getData();
// clean up function when unmounted to avoid getData fired twice problem in React 18
return () => abortController.abort();
}, []);
All you need regarding AbortController with axios here
const controller = new AbortController();
axios.get('/foo/bar', {
signal: controller.signal
}).then(function(response) {
//...
});
// cancel the request
controller.abort()
Since, setState in a functional component do not return a promise, how do we set a loading state and then call an API. I have seen people doing it like the one below. I think the axios call will not wait for the loading state to be successfully set before executing. Is there any other better way to solve this without writing the fetch part in an useEffect with the dependency of the loading state?
useEffect(() => {
const fetchProduct = async () => {
setLoading(true);
try {
const response = await axios('http://localhost/products');
setData(response.data);
} catch (err) {
setError(err);
}
setLoading(false);
};
fetchProduct();
}, [productId]);
you can try something like this
useEffect(() => {
const fetchProduct = async () => {
setLoading(true);
await axios.get('http://localhost/products')
.then(response => {
setLoading(false);
setData(response.data);
}).catch(error => {
setLoading(false);
setError(error);
})
};
fetchProduct();
}, [productId]);
This is my code, I tried bunch of methods but I don't know how to make them work properly :/
I appreciate any kind of help!
const fetchData = React.useCallback(() => {
fetch('http://5sd780beaf65.ngrok.io/api/Data')
.then(response => response.json())
.then(responseJson => {
setData(responseJson);
setFullData(responseJson.results);
setLoading(false);
})
.catch(error => {
console.error(error);
setLoading(false);
setError(err);
});
}, []);
React.useEffect(() => {
let isMounted = true;
fetchData();
}, [fetchData]);
This is probably an issue of using useCallback and not including certain methods internal to your component in the dependency array. For example, you're using what sounds like setState methods in your callback (setData, setFullData, etc.), which I'm guessing are setState methods on the component in question. If something is causing the component to rerender, those methods are reinitialized, and the reference to setFullData (and the component it references) change on rerender. But because you've used useCallback, a newly rerendered component is calling setState methods that belong to a component instance that is stale / no longer mounted.
Solutions: either don't use useCallback, or include all relevant variables and methods in the dependency array:
const fetchData = React.useCallback(() => {
fetch("http://5sd780beaf65.ngrok.io/api/Data")
.then((response) => response.json())
.then((responseJson) => {
setData(responseJson);
setFullData(responseJson.results);
setLoading(false);
})
.catch((error) => {
console.error(error);
setLoading(false);
setError(err);
});
}, [setData, setFullData, setLoading, setError]);
Make your fetchData function asynchronous then add a await before fetch
Sorry if title was a bit unclear, what I want to do is catch the member.send, but I don't know how to use try and catch when also using timeinterval. It gives me an error saying that I haven't handled it.
message.guild.members.forEach(member => {
try {
setInterval(() => {
member.send("hello");
}, 2000)
}
catch(e) {
console.log("couldnt send dm to a user!");
}
Second problem: Cannot read property of 'guild' of undefined, and UnhandledPromiseRejection
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// create an async function and run it
(async function(message) {
for (const [id, member] of message.guild.members) {
try {
// await will throw any error found and allow try/catch to work
await member.send("hello");
} catch (err) {
console.log("Found error", err);
}
await sleep(2000);
}
})();
try/catch doesn't work for promise rejections, nor does it work when wrapped around a setInterval. Just catch the promise:
member.send("hello").catch(err => {
console.error(`Got error ${err}.`);
});
If you want to send a message to each person then the best way is to use a promise-based sleep function:
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// create an async function and run it
(async function() {
for (const [id, member] of message.guild.members) {
try {
// await will throw any error found and allow try/catch to work
await member.send("hello");
} catch (err) {
console.log("Found error", err);
}
await sleep(2000);
}
})();
I'm running an async operation with a callback (ipfs in this case, but it probably doesn't matter), and I'm trying to set state using hooks within that callback. But the code isn't operating...do I need to use useEffect here maybe?
await ipfs.add(buffer, (err, ipfsHash) => {
setIpfsHash(ipfsHash);
console.log("in ipfs - ipfshash", ipfsHash);
});
setIpfsHash is blocking the inner code. The console.log does not run
The way you're handling your ipfs.add function with async/await is incorrect. But yes, you'd want to use React.useEffect in this situation.
If you want to use async/await, you need to create a new promise. In your example, ifps.add(buffer, callback) looks it's taking a callback as an argument, so this won't work.
Instead, turn it into a promise:
function add(buffer) {
return new Promise((resolve, reject) => {
const ipfs = ...
ipfs.add(buffer, (error, ipfsHash) => {
if (error) {
reject(error)
return
}
resolve(ipfsHash)
})
})
}
Then, you can use async/await in your React.useEffect like in the following example:
function App() {
const [buffer, setBuffer] = React.useState(null)
const [ipfsHash, setIpfsHash] = React.useState(null)
React.useEffect(() => {
async function doWork() {
try {
const result = await add(buffer)
setIpfsHash(result)
} catch (error) {
// handle error
}
}
doWork()
}, [buffer])
return (
<div>
...
</div>
)
}
Make sure to specify a dependency array for your useEffect hook so that it only runs when it needs to:
React.useEffect(() => {
// ...
}, [buffer])