React Hooks:在useEffect中使用useState不会在第一次渲染后立即设置状态

时间:2019-09-03 20:45:42

标签: javascript reactjs frontend gatsby

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

逻辑很简单:我将Gatsby.js与React一起使用来渲染<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和{{1}之类的其他状态下,allPics中只有状态tabs未正确设置}是否设置正确?

3 个答案:

答案 0 :(得分:1)

此初始状态加载不需要使用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]))
)

您可以通过这些更改here看到沙盒的工作分支。

答案 1 :(得分:1)

类似于基于类的React组件的生命周期方法,特别是setState的异步特性,功能对等useState钩子也是如此。在当前渲染周期中,挂钩的当前值保持静态,直到“刷新”更新并重新渲染组件。在useEffect钩子中,您尝试访问尚未“同步”的tabs状态值。如果要在此渲染周期中使用该值,则应该直接访问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

答案 2 :(得分:0)

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])