簡體   English   中英

React Hooks:在 useEffect 中使用 useState 不會在第一次渲染后立即設置狀態

[英]React Hooks: using useState inside useEffect doesn't set the state immediately after first rendering

這是我的代碼: https : //codesandbox.io/s/gatsby-starter-default-og782

邏輯很簡單:我使用 Gatsby.js 和 React 來渲染Gallery組件

<StaticQuery
    query={graphql`
      {
        allFile(
          filter: {
            extension: { regex: "/(jpg)|(jpeg)|(png)/" }
            sourceInstanceName: { eq: "images" }
          }
        ) {
          edges {
            node {
              childImageSharp {
                fluid(maxWidth: 800, quality: 95) {
                  aspectRatio
                  src
                  srcSet
                  originalName
                  srcWebp
                  srcSetWebp
                  sizes
                }
              }
            }
          }
        }
      }
    `}
    render={data => (
      <Layout>
        <Gallery minWidth={200} data={data.allFile.edges} />
      </Layout>
    )}
  />

這將為Gallery提供要顯示的圖像文件。

Gallery里面,我有這些狀態

  const [allPics, setAllPics] = useState([]) <-- storing all the image files
  const [currentImgs, setCurrentImgs] = useState([]) <-- storing the images of the current tab
  const [tabs, setTabs] = useState([])

useEffect只在組件掛載時運行一次

useEffect(() => {
    const allPics = data.map((img, i) => {
      img.node.childImageSharp.index = i
      return img.node.childImageSharp
    })
    setAllPics(allPics)
    setTabs(getAllTabs(allPics))
    // default tab when the page first loads
    setCurrentImgs(
      allPics.filter(({ fluid }) => fluid.originalName.includes(tabs[0]))
    )
  }, [data])

然后我通過這個渲染

<>
      <ProjectsContainer>
        {tabs.map(tab => (
          <Project
            key={tab}
            onClick={() => {
              setCurrentImgs(
                allPics.filter(({ fluid }) => fluid.originalName.includes(tab))
              )
            }}
          >
            {tab}
          </Project>
        ))}
      </ProjectsContainer>

      <Mansory gap={"0em"} minWidth={minWidth}>
        {currentImgs.map((img, i) => {
          return (
            <PicContainer
              index={img.index}
              selected={isSelected}
              key={img.index}
            >
              <Enlarger
                src={img.fluid.srcSet.split(" ")[0]}
                enlargedSrc={img.fluid.src}
                index={img.index}
                setIsSelected={setIsSelected}
                onLoad={() => {
                  setTimeout(() => {
                    refs[img.index].current.toggleOpacity(1)
                  }, 300)
                }}
                ref={refs[img.index]}
                realIndex={i}
              />
            </PicContainer>
          )
        })}
      </Mansory>
    </>

問題是,狀態currentImgs好像一直是一個空數組,是useEffect運行后的默認狀態,所以當我們第一次加載頁面的時候,沒有當前圖片可以顯示。 您必須手動單擊這些選項卡中的任何一個以再次觸發setCurrentImgs以顯示當前圖像。

所以問題似乎很明顯,即為什么在useEffect ,只有狀態currentImgs沒有正確設置,而其他狀態如allPicstabs設置正確?

您不需要使用useEffect進行此初始狀態加載。 您可以在useState鈎子中設置狀態的初始值,如下所示:

const [allPics, setAllPics] = useState(
  data.map((img, i) => {
    img.node.childImageSharp.index = i
    return img.node.childImageSharp
  })
)
const getAllTabs = imageObj => {
  return imageObj
    .map(({ fluid }) => fluid.originalName.split("-")[0])
    .filter((name, index, self) => index === self.indexOf(name))
}
const [tabs, setTabs] = useState(getAllTabs(allPics))
const [currentImgs, setCurrentImgs] = useState(
  allPics.filter(({ fluid }) => fluid.originalName.includes(tabs[0]))
)

您可以在此處查看帶有這些更改的沙箱的工作分支。

就像基於類的useState組件的生命周期方法一樣,特別是setState的異步性質,函數對應物useState鈎子也是useState 鈎子的當前值在當前渲染周期中保持靜態,直到更新被“刷新”並重新渲染組件。 在您的useEffect鈎子中,您試圖訪問尚未“同步”的tabs狀態值。 如果您想要此渲染周期中的值,您應該直接訪問選項卡響應數組。

useEffect(() => {
  const allPics = data.map((img, i) => {
    img.node.childImageSharp.index = i
    return img.node.childImageSharp
  })
  setAllPics(allPics)

  // get all tabs
  const tabsArray = getAllTabs(allPics);

  setTabs(tabsArray)

  // default tab when the page first loads
  setCurrentImgs(
    allPics.filter(({ fluid }) => fluid.originalName.includes(tabsArray[0]))
  )
}, []) // pass empty dependency array if you truly only want this to run on mount

React useEffect 使用會話直到每次更新/或者我們可以說生命周期直到更改。 只需從 useState 鈎子傳遞 useEffect 中的參數,它就會更新數據,

不要嘗試傳遞數據變量,它將運行無限循環。 只需通過 setData 函數。

React.useEffect(()=>{
        console.log("effect");

        (async () => {
            try {
                let result = await fetch("/query/countries");
                const res = await result.json();

                let result1 = await fetch("/query/projects");
                const res1 = await result1.json();


                let result11 = await fetch("/query/regions");
                const res11 = await result11.json();
                setData({countries: res, projects: res1, regions: res11});

            } catch {

            }
        })()
    }, [setData])

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM