数组随机播放在开发时有效,但在生产构建时无效 (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
}
}
}
`
信息:
- 这是照片库的索引页。
- Card 是接收和显示封面照片的组件,照片顶部是相册名称,整个 Card 是一个 link 到相册页面。
- 您可以在 https://nicovial.tk 访问此页面,这样您就明白了,洗牌暂时被禁用。
- lodash 是一个 JS 库,在 npm 上有很多数组处理函数。
预期行为:
- 卡片顺序在每次刷新或重新访问时随机排列。
实际行为:
- 封面照片和 link 不会打乱顺序,它们始终按原始顺序显示。
- 但是,专辑标题会打乱顺序,因此卡片显示的名称有误(洗牌时的巧合除外)但 link 是正确的,并且始终按原始顺序显示。
奇怪的细节:
这只发生在生产构建中(本地构建和服务或在线 github/netlify 构建)。当 运行 'gatsby develop' 行为是预期的。
2 项可能有帮助的事情:
您的卡片列表缺少 key
props,这对于 React 在洗牌时保持列表的顺序很重要。
在 Gatsby 产品中,您的 html 已经是 pre-rendered。这意味着当 React 尝试 rehydrate the dom 时,它会发现新的随机列表与 server-rendered 列表不匹配。
您可以通过将索引顺序存储在组件状态中来解决这个问题,然后将其打乱 dependency-less useEffect
或 componentDidMount
。缺点是内容变化会有闪现,可以通过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
}
}
}
`
我的 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
}
}
}
`
信息:
- 这是照片库的索引页。
- Card 是接收和显示封面照片的组件,照片顶部是相册名称,整个 Card 是一个 link 到相册页面。
- 您可以在 https://nicovial.tk 访问此页面,这样您就明白了,洗牌暂时被禁用。
- lodash 是一个 JS 库,在 npm 上有很多数组处理函数。
预期行为:
- 卡片顺序在每次刷新或重新访问时随机排列。
实际行为:
- 封面照片和 link 不会打乱顺序,它们始终按原始顺序显示。
- 但是,专辑标题会打乱顺序,因此卡片显示的名称有误(洗牌时的巧合除外)但 link 是正确的,并且始终按原始顺序显示。
奇怪的细节:
这只发生在生产构建中(本地构建和服务或在线 github/netlify 构建)。当 运行 'gatsby develop' 行为是预期的。
2 项可能有帮助的事情:
您的卡片列表缺少
key
props,这对于 React 在洗牌时保持列表的顺序很重要。在 Gatsby 产品中,您的 html 已经是 pre-rendered。这意味着当 React 尝试 rehydrate the dom 时,它会发现新的随机列表与 server-rendered 列表不匹配。
您可以通过将索引顺序存储在组件状态中来解决这个问题,然后将其打乱 dependency-less useEffect
或 componentDidMount
。缺点是内容变化会有闪现,可以通过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
}
}
}
`