NodeJS 在React项目中使用www.example.com获取无限循环Axios.post

zxlwwiss  于 2023-03-22  发布在  Node.js
关注(0)|答案(1)|浏览(111)

我在React应用程序中有一个页面/收藏夹,我的Axios.post请求setFavArray在记录它时给了我一个无限循环,它不在useEffect中,当它在时,数组只在随机刷新时填充。React新手,你知道为什么post或useState会导致这种情况吗?

function Favorites() {
    // console.log(login_State)
    const [loginState, setLoginState] = useState("");
    // const [favoriteID, setFavoriteID] = useState([]);
    // const [favResult, setFavResults] = useState();
    const [favArray, setFavArray] = useState();
    const [resultsData, setResultsData] = useState([]);

    Axios.post("http://localhost:3001/favorites", {
            username: loginState
        })
        .then((response) => {
            setFavArray(response)
            // console.log("FAV ARRAY: ", favArray)
        })

    useEffect(() => {
        Axios.get("http://localhost:3001/login").then((response) => {
            if (response.data.loggedIn === true) {
                setLoginState(response.data.user[0].username)
                // console.log(loginState)
            }
        })

        // fetch('https://api.spoonacular.com/recipes/644366/information?apiKey=blank')
        //   .then(response => response.json())
        //   .then((data) => {
        //       setResultsData(data)
        //       // console.log("REAL: ", resultsData)
        //   });
    }, []);

    console.log("FAV ARRAY: ", favArray)
    console.log("REAL SHIT: ", resultsData)

    return (
        resultsData.map(item => {
            return (
                <div className="favorites-wrapper">
                    <h1> All of your favorites in one place! </h1>
                    <div className="favorite-card">
                    <Col className='mt-5' xs={6} md={4}>
                        <Card className='card-hover'>
                            <Card.Img fluid className='img' variant="top" src={item.image} />
                            <Card.Body className='card-body'>
                                <Card.Title className='title' >{item.title}</Card.Title>
                                <Card.Text>
                                    Some quick example text to build on the card title and make up the
                                    bulk of the card's content.
                                </Card.Text>
                            </Card.Body>
                        </Card>
                    </Col>
                </div>
            </div>
            )
        })
    )
}

export default Favorites;

我试着移动每个调用进出useEffect,没有运气。在我的后端,我只是检查一个DB,看看当前用户名有什么“收藏夹”。

oknwwptz

oknwwptz1#

问题
你会得到一个无限循环,因为每次你的组件被渲染时你都会触发post请求,这反过来又会改变组件的状态,这反过来又会触发另一个渲染。
目前,以下情况正在逐步发生:
1.第一次调用组件
1.将触发发布请求

  1. useEffect被触发,这反过来又触发了getlogin请求
    1.您正在记录FAV ARRAYREAL SHIT(这两个请求仍然是空的,因为这两个请求都还没有完成)
    1.由于resultsData是空的,所以您构建并返回JSX(实际上什么都没有)。
    1.一段时间后(两个请求中的第一个请求完成所需的时间),调用setFavArraysetLoginState,这将触发组件重新呈现,因此这些步骤重新开始。
    为了避免无限循环,您必须确保只触发一次请求。此外,您对favourits的POST调用取决于登录调用的结果,这也是目前没有正确处理的。这可能导致您的数据有时只被正确获取(这是一个竞争条件)。

解决方案

让我们来看看如何实现这一点。首先,让我们来处理调用。因为我们需要第一次调用的结果来执行第二次调用,所以我们需要等待第一次调用完成。(这在async/await中更容易,详见文章末尾)。我还建议将这部分移到一个单独的函数中。请参阅这里:

const fetchFavourits = () => {
    const response = await Axios.get("http://localhost:3001/login");
    if (response.data.loggedIn !== true) {
        return []; // if the user isn't logged in, you probably want to throw an error or handle this somehow. For the demo, we just return an empty array.
    }
    const username = response.data.user[0].username;
    return await Axios.post("http://localhost:3001/favorites", {
        username: username 
    });
}
  • 注意:这个函数基本上可以是异步发生的任何事情。*

回到组件中的主要问题。要在组件第一次渲染时触发一次请求,我们可以使用useEffect和一个空的依赖数组。请参见这里:

function Favorites() {
    const [loaded, setLoaded] = useState(false); // Keep track whether the data has been loaded. Feel free to remove it if you don't need it.
    const [data, setData] = useState([]);
    
    useEffect(() => {
        // to call an async function in useEffect we need this workaround
        // see end of post for details
        const fetchData = async () => {
            setData( await fetchFavourits() );
            setLoaded(true);
            // The two lines above are basically the same as
            // fetchFavourites().then((data) => { 
            //     setData(data); 
            //     setLoaded(true); 
            // });
        };
        fetchData();
    }, []); // any empty dependencies array will make sure that the useEffect is only called on first render.

    // While the data isn't loaded, you can display some loading indicator
    if( !loaded )
        return <p>Still loading</p>;

    // Now we can be sure that data has finished loading
    console.log(data);

    return <div>
        /* your jsx here */
    </div>
}

工作原理

让我们再一步一步地看一下:
1.组件第一次被调用

  1. useEffect被调用,反过来又调用fetchFavourites()。(JS实际上并没有等待它完成。fetchData函数本质上是暂停的,有时会在组件的其余部分执行完后继续执行)
  2. loaded是false,所以我们返回这个loading jsx。
    1.* 一段时间过去,fetchFavourites完成 *
  3. setDatasetLoaded被调用。(这些会改变组件的状态,这将使react重新呈现它)
    1.组件被调用/呈现
    1.我们跳过useEffect,因为它的依赖项没有改变
  4. loaded现在为true,所以我们继续
    1.您使用实际数据呈现组件

更好的解决方案

另一种解决问题的方法(我强烈推荐)是使用类似TanStack Query的库,这样的库可以解决获取和处理服务器状态的很多痛点。

继续阅读

有关async/await的详细信息,请参阅https://stackoverflow.blog/2019/09/12/practical-ways-to-write-better-javascript/How and when to use ‘async’ and ‘await’。它本质上与promise.then相同,但更具可读性。
有关useEffect中的async函数的更多详细信息,请参阅以下内容:React Hook Warnings for async function in useEffect: useEffect function must return a cleanup function or nothing

相关问题