数组随机播放在开发时有效,但在生产构建时无效 (React/Gatsby)

Array shuffle working while developing but not on production build (React/Gatsby)

我的 gatsby 网站上有这个 React 组件:

import React from 'react'
import Layout from '../components/layout'
import SEO from '../components/seo'
import { graphql } from "gatsby"
import Card from '../components/Card'
import Gallery from '../components/Gallery'
import shuffle from 'lodash/shuffle'

const IndexPage = ({data}) => {
  function linkify(string){
    return string.toLowerCase().replace(/["'()]/g,"").replace(/\s/g, '-');
  }
  return(
  <Layout>
    <SEO />
    <Gallery>
      {
      shuffle(data.allContentfulAlbum.nodes).map((album) => {
        return (
        <Card src={album.cover.file.url} title={album.title} link={'/album/'+linkify(album.title)} />
        )
        })}
    </Gallery>
  </Layout>
)
    }

export default IndexPage

export const query = graphql`
query Albums {
  allContentfulAlbum {
    nodes {
      cover {
        file {
          url
        }
      }
      title
    }
  }
}
`

信息:

预期行为:

实际行为:

奇怪的细节:

这只发生在生产构建中(本地构建和服务或在线 github/netlify 构建)。当 运行 'gatsby develop' 行为是预期的。

2 项可能有帮助的事情:

  • 您的卡片列表缺少 key props,这对于 React 在洗牌时保持列表的顺序很重要。

  • 在 Gatsby 产品中,您的 html 已经是 pre-rendered。这意味着当 React 尝试 rehydrate the dom 时,它会发现新的随机列表与 server-rendered 列表不匹配。

您可以通过将索引顺序存储在组件状态中来解决这个问题,然后将其打乱 dependency-less useEffectcomponentDidMount。缺点是内容变化会有闪现,可以通过CSS管理,但要考虑SEO

如果 SEO 无关紧要,您可以简单地呈现任何内容,直到订单被打乱。

正如@derek-nguyen 指出的那样,我必须在运行时进行洗牌(使用 useLayoutEffect),将洗牌后的数组存储在组件的状态中。

需要指出的重要一点是,使用 lodash 将不起作用,因为 'shuffler' 函数需要在组件中声明,以便在运行时可用。

另外,shuffler函数不能更新原来的数组,会因为问题,需要return一个新的。示例:

此函数不会工作:

  function shuffleArray(array) {
  for (let i = array.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1));
      [array[i], array[j]] = [array[j], array[i]];
  }
  }

这个会很好用:

function shuffleArray(array) {
  let tempArray = array;
  for (let i = tempArray.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1));
      [tempArray[i], tempArray[j]] = [tempArray[j], tempArray[i]];
  }
  return tempArray;
}

这是更新后的组件,现在正在制作生产版本:

import React, { useLayoutEffect, useEffect, useState } from 'react'
import Layout from '../components/layout'
import SEO from '../components/seo'
import { graphql } from "gatsby"
import Card from '../components/Card'
import Gallery from '../components/Gallery'

function linkify(string){
  return string.toLowerCase().replace(/["'()]/g,"").replace(/\s/g, '-');
}
function shuffleArray(array) {
  let tempArray = array;
  for (let i = array.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1));
      [array[i], array[j]] = [array[j], array[i]];
  }
  return tempArray;
}
const IndexPage = ({data}) => {
  const [shuffled,setShuffled] = useState([]);
  useLayoutEffect(() => {
    setShuffled(shuffleArray(data.allContentfulAlbum.nodes));
  })
  return(
  <Layout>
    <SEO />
    <Gallery>
      {
      shuffled.map((album, index) => {
        return (
        <Card src={album.cover.file.url} title={album.title} link={'/album/'+linkify(album.title)} key={index} />
        )
        })}
    </Gallery>
  </Layout>
)
    }

export default IndexPage

export const query = graphql`
query Albums {
  allContentfulAlbum {
    nodes {
      cover {
        file {
          url
        }
      }
      title
    }
  }
}
`