如何循环使用 useEffect 和 setState

Posted

技术标签:

【中文标题】如何循环使用 useEffect 和 setState【英文标题】:How to loop in useEffect and setState 【发布时间】:2021-11-04 20:08:06 【问题描述】:

我有类别,每个类别都有子类别,我想 setState 像一个字典

category1.name: [/*list of all subcategories*/], category2.name: [...]

我有

useEffect(()=>
        Axios.get("http://localhost:3001/categories/get").then((p) =>  /* return a list of categories */
            setCategories(p.data)
            let actual = 
            for (let i = 0; i < p.data.length; i++) 
                Axios.get("http://localhost:3001/category/subcategories",  /* return all the subcategories of p.data[i], this is working! */
                params : category : p.data[i].category).then((p) => 
                    actual = ...actual, [p.data[i].category]: p.data
                    console.log(actual) /* 1 */
                )
            
            console.log(actual) /* 2 */
        )
    , [])

我已经让后端工作了,但是当我 console.log(actual) /* 2 */ 它只是一个空字典 ;当我console.log(actual) /* 1 */ 不是空的(只有 1 个元素),我不知道我什么时候可以setState(actual),有人知道我想要做什么吗?

【问题讨论】:

这里你需要使用promise,作为你的第二个控制台,即console.log(actual) /* 2 */在第一个控制台之前执行。所以它打印一个空对象。 【参考方案1】:

您可以使用async/await 来按您的需要工作。

编辑(@skyboyer 提到):

当您进行网络调用时,所有请求将按顺序进行,加载将花费 N×time_to_load_one

试试这个。

useEffect(()=>
        Axios.get("http://localhost:3001/categories/get").then(async (p) =>  /* return a list of categories */
            setCategories(p.data)
            let actual = 
            for (let i = 0; i < p.data.length; i++) 
                await Axios.get("http://localhost:3001/category/subcategories",  /* return all the subcategories of p.data[i], this is working! */
                params : category : p.data[i].category).then((p) => 
                    actual = ...actual, [p.data[i].category]: p.data
                    console.log(actual) /* 1 */
                )
            
            console.log(actual) /* 2 */
        )
    , [])

【讨论】:

我认为值得一提的是,所有请求都会按顺序进行,加载需要 N×time_to_load_one【参考方案2】:

这可能会让你走上正轨

useEffect(() => 
    (async() => 
        const p = await Axios.get("http://localhost:3001/categories/get")
        setCategories(p.data)
        let actual = 
        for (let i = 0; i < p.data.length; i++) 
            const x = await Axios.get("http://localhost:3001/category/subcategories",  /* return all the subcategories of p.data[i], this is working! */
                params: 
                    category: p.data[i].category
                
            )

            actual = ...actual, [x.data[i].category]: x.data
            
            console.log(actual) /* 1 */

        
        console.log(actual) /* 2 */
    )();
, [])

同时避免像 p 那样重复使用相同的变量,这会使代码更难阅读。

【讨论】:

【参考方案3】:

在我看来,您需要花时间学习 JS 中的 promiseasync/await。请原谅我不能花时间写一整篇文章来教所有东西,请谷歌这些关键字。

为了补充使用 async/await 的 @HarshPatel's answer,下面是使用 promise 的解决方案。他的解决方案按顺序发出请求,而我的解决方案同时发出请求。

useEffect(() => 
    Axios.get("http://localhost:3001/categories/get").then((p) => 
        /* return a list of categories */
        setCategories(p.data)

        const tasks = p.data.map((category) => 
            return Axios.get(
                "http://localhost:3001/category/subcategories",
                 params:  category  
            ).then((p) => 
                return  [category]: p.data 
            )
        )
        
        Promise.all(tasks).then(results => 
            const actual = results.reduce((acc, item) => 
                return  ...acc, ...item 
            , )
        )

        setState(actual)
    )
, [])

【讨论】:

以上是关于如何循环使用 useEffect 和 setState的主要内容,如果未能解决你的问题,请参考以下文章

如何在useEffect中停止无限循环?

尽管依赖项没有变化,useEffect 运行无限循环

如何在useeffect钩子中停止无限循环

while 循环在 useEffect 挂钩中不起作用

useEffect 与 useContext 陷入无限循环

面试官:如何解决React useEffect钩子带来的无限循环问题