I'm trying to call an async function in a callback in useEffect like this.
import {useState, useEffect} from 'react';
import Navbar from 'react-bootstrap/Navbar';
interface EnBoards {
id: number
name: string
uri: string
}
const RedichanNav = (): JSX.Element => {
const [enBoards, setEnBoards] = useState({});
useEffect(() => {
const fetchEnBoards = async () => {
const response = await fetch('/api/en-boards');
const enBoardsJson = await response.json() as EnBoards;
setEnBoards(enBoardsJson);
};
fetchEnBoards(); // Here
});
return (
<Navbar ></Navbar>);
};
export default RedichanNav;
Then I got an error.
20:5 error Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
@typescript-eslint/no-floating-promises
Then I changed the code like this.
useEffect(async () => {
const fetchEnBoards = async () => {
const response = await fetch('/api/en-boards');
const enBoardsJson = await response.json() as EnBoards;
setEnBoards(enBoardsJson);
};
await fetchEnBoards();
});
Then I got another error.
14:13 error Effect callbacks are synchronous to prevent race conditions. Put the async function inside:
useEffect(() => {
async function fetchData() {
// You can await here
const response = await MyAPI.getData(someId);
// ...
}
fetchData();
}, [someId]); // Or [] if effect doesn't need props or state
Learn more about data fetching with Hooks: react-hooks/exhaustive-deps
My code is almost the same as FAQ and small demo and this article.
My .eslintrc.js
module.exports = {
env: {
browser: true,
es2021: true,
},
extends: [
'plugin:react/remended',
'airbnb',
'airbnb/hooks',
'plugin:@typescript-eslint/remended',
'plugin:@typescript-eslint/remended-requiring-type-checking',
'prettier',
],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 12,
sourceType: 'module',
tsconfigRootDir: __dirname,
project: ['./tsconfig.json'],
},
plugins: [
'react',
'@typescript-eslint',
],
"ignorePatterns": [
".eslintrc.js"
],
rules: {
'semi': ['error', 'always'],
'no-use-before-define': "off",
"@typescript-eslint/no-use-before-define": "off",
'import/prefer-default-export': "off",
'import/extensions': [
'error',
{
js: 'never',
jsx: 'never',
ts: 'never',
tsx: 'never',
},
],
'react/jsx-filename-extension': [
'error',
{
extensions: ['.jsx', '.tsx'],
},
],
'react/react-in-jsx-scope': 'off',
'no-void': [
'error',
{
allowAsStatement: true,
},
],
"react/function-ponent-definition": [
2,
{
"namedComponents": "arrow-function"
}
]
},
settings: {
'import/resolver': {
node: {
paths: ['src'],
extensions: ['.js', '.jsx', '.ts', '.tsx']
},
},
},
};
Enmironment
- MacOS 12.5
- Node.js 18.7.0
- TypeScript 4.8.4
- React 18.2.0
- React-Bootstrap 2.5.0
- ESLint 8.25.0
- @typescript-eslint/eslint-plugin 5.39.0
- eslint-plugin-react-hooks 4.6.0
Thank you to read. Can anyone solve this?
I'm trying to call an async function in a callback in useEffect like this.
import {useState, useEffect} from 'react';
import Navbar from 'react-bootstrap/Navbar';
interface EnBoards {
id: number
name: string
uri: string
}
const RedichanNav = (): JSX.Element => {
const [enBoards, setEnBoards] = useState({});
useEffect(() => {
const fetchEnBoards = async () => {
const response = await fetch('/api/en-boards');
const enBoardsJson = await response.json() as EnBoards;
setEnBoards(enBoardsJson);
};
fetchEnBoards(); // Here
});
return (
<Navbar ></Navbar>);
};
export default RedichanNav;
Then I got an error.
20:5 error Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
@typescript-eslint/no-floating-promises
Then I changed the code like this.
useEffect(async () => {
const fetchEnBoards = async () => {
const response = await fetch('/api/en-boards');
const enBoardsJson = await response.json() as EnBoards;
setEnBoards(enBoardsJson);
};
await fetchEnBoards();
});
Then I got another error.
14:13 error Effect callbacks are synchronous to prevent race conditions. Put the async function inside:
useEffect(() => {
async function fetchData() {
// You can await here
const response = await MyAPI.getData(someId);
// ...
}
fetchData();
}, [someId]); // Or [] if effect doesn't need props or state
Learn more about data fetching with Hooks: https://reactjs/link/hooks-data-fetching react-hooks/exhaustive-deps
My code is almost the same as FAQ and small demo and this article.
My .eslintrc.js
module.exports = {
env: {
browser: true,
es2021: true,
},
extends: [
'plugin:react/remended',
'airbnb',
'airbnb/hooks',
'plugin:@typescript-eslint/remended',
'plugin:@typescript-eslint/remended-requiring-type-checking',
'prettier',
],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 12,
sourceType: 'module',
tsconfigRootDir: __dirname,
project: ['./tsconfig.json'],
},
plugins: [
'react',
'@typescript-eslint',
],
"ignorePatterns": [
".eslintrc.js"
],
rules: {
'semi': ['error', 'always'],
'no-use-before-define': "off",
"@typescript-eslint/no-use-before-define": "off",
'import/prefer-default-export': "off",
'import/extensions': [
'error',
{
js: 'never',
jsx: 'never',
ts: 'never',
tsx: 'never',
},
],
'react/jsx-filename-extension': [
'error',
{
extensions: ['.jsx', '.tsx'],
},
],
'react/react-in-jsx-scope': 'off',
'no-void': [
'error',
{
allowAsStatement: true,
},
],
"react/function-ponent-definition": [
2,
{
"namedComponents": "arrow-function"
}
]
},
settings: {
'import/resolver': {
node: {
paths: ['src'],
extensions: ['.js', '.jsx', '.ts', '.tsx']
},
},
},
};
Enmironment
- MacOS 12.5
- Node.js 18.7.0
- TypeScript 4.8.4
- React 18.2.0
- React-Bootstrap 2.5.0
- ESLint 8.25.0
- @typescript-eslint/eslint-plugin 5.39.0
- eslint-plugin-react-hooks 4.6.0
Thank you to read. Can anyone solve this?
Share Improve this question edited Oct 23, 2022 at 15:47 skyboyer 23.8k7 gold badges62 silver badges71 bronze badges asked Oct 23, 2022 at 6:00 yukiyuki 3334 silver badges12 bronze badges 3-
Your first attempt was mostly right. Replace
fetchEnBoards();
withfetchEnBoards().then(() => {}).catch(err => console.error(err));
and you should be good – Derek Commented Oct 23, 2022 at 6:14 -
You'll also want to return a function from that useEffect() that cancels the request on unmount if it's still active. You'll need to pass in an AbortController signal to the
fetch
method and call its.abort()
method in that returned function. – Derek Commented Oct 23, 2022 at 6:16 -
fetchEnBoards().then(() => {}).catch(err => console.error(err));
This solution worked. Thank you very much! – yuki Commented Oct 23, 2022 at 8:25
4 Answers
Reset to default 2This is not an error ing from React itself. It is ing from TypeScript, or more specifically, typescript-eslint
.
Your first code would run without any exceptions, if it was written in JavaScript, and you didn't activate the eslint-plugin-no-floating-promise
rule.
Here's the typescript-eslint
documentation about floating promises: https://github./typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-floating-promises.md
Here's an article about the same: https://mikebifulco./posts/eslint-no-floating-promises
Why this error?
When you call an async function in your code, it returns a Promise. It's on you to handle the result of the promise using
- async/await
- then/catch
If you're using neither, it means that you're calling the async function, and are simply not waiting for it to resolve or reject. Your code moves on assuming that everything is fine. This is a floating promise, and your eslint is simply forcing you to handle the Promise.
What happens when you disable the eslint rule and don't handle the Promise?
As long as your asynchronous code resolves as expected, nothing. When one of them fails, and the Promise is rejected, you'll get an uncaught exception. Not quite desirable.
The first time you wrote the code it was right. Reading the error you got seems like you needed to add an try catch block to you function, like:
useEffect(() => {
const fetchEnBoards = async () => {
try { //This
const response = await fetch('/api/en-boards');
const enBoardsJson = await response.json() as EnBoards;
setEnBoards(enBoardsJson);
catch(err) {
console.log(err)
}
};
fetchEnBoards(); // Here
});
useEffect(() => {
fetch('/api/en-boards')
.then(res => res.json() as EnBoards)
.then(setEnBoards);
}, []);
Note that the empty array passed to useEffect
will result in the API call only being made once, instead of on every update.
Looks like you need to read async await
useEffect(() => {
const fetchEnBoards = async () => {
const response = await fetch('/api/en-boards');
const enBoardsJson = await response.json() as EnBoards;
setEnBoards(enBoardsJson);
};
fetchEnBoards()
.then((response)=>
console.log("In then block"))
.catch((err)=>
console.log("In catch block"))
});