我的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
}
}
}
`
信息:
- 这是照片库的索引页
- 卡片是接收并显示封面照片的组件,照片顶部的相册名称,整个卡片是指向相册页面的链接
- 您可以访问此页面https://nicovial.tk所以你明白了,洗牌现在是禁用的
- lodash是一个JS库,在npm中有许多可用的数组处理函数
预期行为:
- 每次刷新页面或重新访问时都会打乱卡片顺序
实际行为:
- 封面照片和链接不会被打乱,它们总是按原始顺序显示
- 然而,专辑标题是混洗的,所以卡片显示的名称是错误的(除了混洗时的巧合(,但链接是正确的,并且总是按原始顺序
奇怪的细节:
这只发生在生产版本上(本地版本&serve或在线github/netlify版本(。当运行"gatsby-develop"时,行为是预期的行为。
两件可能有帮助的事情:
-
您的卡牌列表缺少
key
道具,这对于React在洗牌时保持列表的有序性很重要。 -
在Gatsby制作中,您的html已经预先呈现。这意味着当React试图重新水合dom时,它会发现新的随机列表与服务器呈现的列表不匹配。
您可以通过将索引顺序存储在组件状态中,然后在无依赖的useEffect
或componentDidMount
中对其进行混洗来解决此问题。缺点是会有一个闪光的内容变化,可以通过CSS进行管理,但SEO必须考虑在内。
如果SEO不重要,你可以简单地不渲染任何东西,直到顺序被打乱。
正如@derek nguyen所指出的,我必须在运行时进行shuffle(使用useLayoutEffect(,将shuffle数组存储在组件的状态中。
需要指出的一点是,使用lodash将不起作用,因为"shuffler"函数需要在组件中声明,以便在运行时可用。
此外,shuffler函数不能更新原始数组,它会因为问题,需要返回一个新数组。示例:
此功能将NOT工作:
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
}
}
}
`