为什么我的函数无法读取组件状态的实际值?

时间:2019-10-17 21:28:10

标签: javascript reactjs q

对于一个学校项目,我的团队正在构建一个表,该表通过数据库调用填充了城市数据。 表格组件的骨架是这样的:

import React, { useState } from 'react' 

function Table(props) {
  const [ cities, setCities ] = useState([])
  const [ pageNum, setPageNum ] = useState(0)

  useEffect(()=> { // this sets up the listener for the infinite scroll
    document.querySelector(".TableComponent").onscroll = () => {
      if (Math.ceil(table.scrollHeight - table.scrollTop) === table.clientHeight) showMoreRows()
    }
    //initial fetch
    fetchData(0)
  },[])

  async function showMoreRows() {
    console.log("Show more rows!")
    await fetchData(pageNum)
  }

  async function fetchData(page) {
    // some code, describing fetching
    // EDIT2 start
    console.log(pageNum)
    // EDIT2 end
    const jsonResponse = await {}// THE RESPONSE FROM THE FETCH
    if(page) {
      setCities([...cities, ...jsonResponse])
      console.log("page is true", page)
      setPageNum(pageNum + 1)
    } else {
      console.log("page is false", page) // this always runs and prints "page is false 0"
      setCities([...cities, ...jsonResponse])
      setPageNum(1)
    }
  }


  return <div className="TableComponent"> { pageNum } 
           <!-- The rest of the component --> 
         </div>
}

该表具有“无限滚动”功能,因此,当您滚动到页面底部时,它将打印"Show more rows!"并运行fetchData(pageNum)以获取更多数据。此时,在初始获取后,pageNum变量应为1或更大,但是由于某种原因,该函数的作用就像是0。我将pageNum变量放在JSX,我可以看到它是1,但是无论我何时运行它,它仍然会打印出"page is false 0"

当我尝试搜索该问题时,似乎唯一相似的事情可能是我尝试在使用setPageNum之后(重绘之前)过早读取useState-variable,但就目前而言并非如此我可以看到。我在尝试之间花了很多时间,它总是说pageNum为零。

关于我在做什么错的任何想法,以及这在任何方面如何有意义?

感谢您的帮助!

编辑:刚刚尝试了我写过的代码,它似乎可以工作-但是我拥有的完整代码不起作用。即使上面的代码可行,对这个问题有任何想法吗?

EDIT2:我在console.log(pageNum)函数中添加了fetchData,并进行了一些测试,看来我在useState(VALUE)的初始值中输入的内容都是正在打印的内容。这对我来说很有意义。 帮助。

EDIT3:添加了一个等待,已经在真实代码中

EDIT4:我已经尝试了一段时间,但是意识到当我使用react时,我可以将我拥有的滚动侦听器移到JSX区域,然后由于某些原因它起作用了。现在可以使用了。不能真正将任何答案标记为正确的答案,但问题已得到解决。

感谢所有尝试提供帮助的人,非常感谢。

3 个答案:

答案 0 :(得分:1)

由于您不了解您对组件状态的依赖性,因此发生了陈旧问题。

例如,useEffect确保在每次滚动时都调用来自初始渲染范围的showMoreRows的值。 showMoreRows的此副本引用pageNum的初始值,但是该值在closure中与函数一起被“冻结”,并且在组件状态不变时不会更改。因此,滚动侦听将无法正常工作,因为它需要知道pageNum的当前状态。

您可以通过使用callbacks来“挂钩” showMoreRowsfetchData并声明它们对组件状态的依赖性来解决问题。然后,您必须声明useEffect对这些回调的依赖性,并使用clean-up function处理多次调用的效果。

它看起来像这样(我没有尝试运行代码):

import React, { useState } from 'react' 

function Table(props) {
  const [ cities, setCities ] = useState([])
  const [ pageNum, setPageNum ] = useState(0)

  useEffect(()=> { 
    // Run this only once by not declaring any dependencies
    fetchData(0)
  }, [])

  useEffect(()=> {
    // This will run whenever showMoreRows changes.

    const onScroll = () => {
      if (Math.ceil(table.scrollHeight - table.scrollTop) === table.clientHeight) showMoreRows()
    };

    document.querySelector(".TableComponent").onscroll = onScroll;

    // Clean-up
    return () => document.querySelector(".TableComponent").onscroll = null;
  }, [showMoreRows])

  const showMoreRows = React.useCallback(async function () {
    console.log("Show more rows!")
    await fetchData(pageNum)
  }, [fetchData, pageNum]);

  const fetchData = React.useCallback(async function (page) {
    // some code, describing fetching
    // EDIT2 start
    console.log(pageNum)
    // EDIT2 end
    const jsonResponse = await {}// THE RESPONSE FROM THE FETCH
    if(page) {
      setCities([...cities, ...jsonResponse])
      console.log("page is true", page)
      setPageNum(pageNum + 1)
    } else {
      console.log("page is false", page) // this always runs and prints "page is false 0"
      setCities([...cities, ...jsonResponse])
      setPageNum(1)
     }
  }, [setCities, cities, setPageNum, pageNum]);

  return <div className="TableComponent"> { pageNum } 
           <!-- The rest of the component --> 
         </div>
}

答案 1 :(得分:0)

这可能无法完全解决问题(如果没有更多上下文,很难说清),但是useEffect运行every render,因此像“ initial” fetchData(0)这样的事情将在每次更新时运行,可能会在page = 0中的那个条件下每次给您fetchData的结果。

答案 2 :(得分:0)

没有更多上下文很难说,但我有一个猜测。 尝试使用

setCities(value => [...value, ...jsonResponse])

代替

setCities([...cities, ...jsonResponse])

还要确保您使用等待来解决请求的承诺,例如:

const jsonResponse = await ...

您可以在控制台上对其进行日志记录,以检查它们是否未挂起,并且如果它是嵌套对象,则可以获取正确的属性。