bengal
bengal

Reputation: 331

Calling async function in useEffect causes an ESLint error

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.org/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/recommended',
   'airbnb',
   'airbnb/hooks',
   'plugin:@typescript-eslint/recommended',
   'plugin:@typescript-eslint/recommended-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-component-definition": [
       2,
       {
         "namedComponents": "arrow-function"
       }
     ]
 },
 settings: {
   'import/resolver': {
     node: {
       paths: ['src'],
       extensions: ['.js', '.jsx', '.ts', '.tsx']
     },
   },
 },
};

Enmironment

Thank you to read. Can anyone solve this?

Upvotes: 6

Views: 8183

Answers (4)

JBallin
JBallin

Reputation: 9807

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.

Upvotes: 0

This is not an error coming from React itself. It is coming 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.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-floating-promises.md

Here's an article about the same: https://mikebifulco.com/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

  1. async/await
  2. 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.

Upvotes: 2

user11877521
user11877521

Reputation: 331

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"))
  });

Upvotes: -1

Davi Oliveira
Davi Oliveira

Reputation: 81

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
      });

Upvotes: 0

Related Questions