Home > Software engineering >  using useState async makes webapp render unlimited times
using useState async makes webapp render unlimited times

Time:09-09

Here I have a useState for posts of my blog webapp. I am getting the posts from the mongoose back end which works fine. But the second I set the posts variable I get an infinite loop of rerendering.

Here is the app.jsx:

import React, {useState} from 'react';
import './App.css';
import Nav from './Navbar/NavBar.jsx';
import Content from "./content/Content"
import { getPosts } from "./api/post";

function App() {

    const idList = ["631a58c165b3a10ac71497e1", "631a58fa65b3a10ac71497e3"];

    const [posts, setPosts] = useState([]);

    setPosts(async() => await getPosts(idList));

  return (
    <div>
        hello
    </div>
  );
}

export default App;

Here is the axios part:

import axios from "axios";


const getPost = (id) => {
    new Promise((resolve, reject) => {
        const url = "/posts/"   id
        console.log(url)
        axios.get(url)
            .then(response => {
                if (response.status === 200){
                    console.log("Succesfull call");
                    console.log(response.data);
                    resolve(response.data);
                }
                else if(response.status === 404){
                    reject(response);
                }
            })
            .catch(err => {
                console.log("Failed call");
                reject(err);
            })
    });
};

const getPosts = async (idList) => {
    var result = []
    for(const id in idList){
        try{
            let post = await getPost(idList[id]);
            result.push(post);
        }catch(err){
            console.log(err.message);
        }
    }
    
    if (result.length === 0) throw {message: "No posts"};
    else return result;

};

export {getPosts};

How can I run setPosts async so that the site wont refresh infinitely?

CodePudding user response:

You cannot pass a function returning a promise into the state setter. It must return the new state value directly.

You'll want to use an effect hook instead

useEffect(() => {
  getPosts(idList).then(setPosts);
}, []); // empty dependency array means run once on mount

Given idList appears constant, you should define it outside your component. Otherwise you'll need to work around its use as a dependency in the effect hook.


Your getPosts function falls prey to the explicit promise construction anti-pattern. Since Axios already returns a promise, you don't need to make a new one.

You can also use Promise.allSettled() to make your requests in parallel.

const getPost = async (id) => (await axios.get(`/posts/${id}`)).data;

const getPosts = async (idList) => {
  const results = (await Promise.allSettled(idList.map(getPost)))
    .filter(({ status }) => status === "fulfilled")
    .map(({ value }) => value);

  if (results.length === 0) {
    throw new Error("No posts");
  }
  return results;
};
  • Related