盖茨比静态/动态混合组件

时间:2018-10-23 23:35:52

标签: javascript reactjs gatsby

我对gatsby developgatsby build生成的站点之间的行为不一致有问题。结果是一个可以在开发中工作但不能在生产中工作的站点。

我的网站摘要:

一个简单的类似博客的网站(个人资料而不是博客文章)。指标 页面是一个人的列表,列表中的每个项目都链接到该人的个人资料页面。

我正在使用Gatsby构建网站。我的数据(个人资料)是托管在Contentless无头CMS上的条目。我正在使用gatsby-source-contentful源插件。

问题的高级描述

我无法在索引页面上随机显示配置文件列表项的顺序。我的网站超越任何基本的gatsby教程的唯一行为是,我希望随机分配索引页面上的配置文件列表(以使每个人都有机会被列在顶部)。

gatsby build生成一个静态索引页面,该列表具有一个排列的列表。 当加载到浏览器中时,ThumbList组件会将这些项目重新改组为渲染上的另一个排列,并且某些子元素无法通过react适当地管理,并随着其他元素的移动而停留在原处。例如,这会导致配置文件图像与错误的名称配对。

代码

为便于阅读,以下代码做了一些总结。

src / pages / index.js:

import React from "react"
import Layout from "../components/layout"
import ThumbList from "../components/thumbList"
import { graphql } from "gatsby"

export default ({data}) => {
  // people are called "creators" in the app
  const creatorData = data.allContentfulCreator.edges
  const shuffledData = shuffle(creatorData.slice(0))
    return (
      <Layout>
        <ThumbList data={shuffledData} />
      </Layout>
    )
}

const shuffle = (a) => {
  // Fisher-Yates randomized array in-place shuffle algo
  // ...
  return a
}

export const query = graphql`
  {
    allContentfulCreator {
      edges {
        node {
          id
          slug
          name
          bio {
            id
            bio
          }
          mainImage {
            file {
              url
            }
          }
        }
      }
    }
  }
`

src / components / thumbList.js:

import React from "react"
import { Link } from "gatsby"

// A list of creator profile links, with name and picture thumbnail
export default ({data}) => {
  return (
    <div>
      <ul>
        {
          data.map(({node}) => {
            const creator = node
            const link = "/" + creator.slug
            const image = "https:" + creator.mainImage.file.url

            return (
              <li key={creator.id}>
                <Link to={link}>
                  {creator.name}
                </Link>
                <img src={image} />
              </li>
            )
          })
        }
      </ul>
    </div>
  )
}

gatsby build的结果是包含以下内容的index.html

  <ul>
    <li>
      <a href="/alice">
        Alice
      </a>
      <img src="cdn.com/alice.jpg">
    </li>
    <li>
      <a href="/bob">
        Bob
      </a>
      <img src="cdn.com/bob.jpg">
    </li>
    <li>
      <a href="/eve">
        Eve
      </a>
      <img src="cdn.com/eve.jpg">
    </li>
  </ul>

但是,当在浏览器中(通过gatsby serve或网站的部署版本)查看索引页时,实时反应ThumbList组件会再次在其render方法中重新整理数据。

结果重新呈现为html:

  <ul>
    <li>
      <a href="/alice">
        Bob
      </a>
      <img src="cdn.com/alice.jpg">
    </li>
    <li>
      <a href="/bob">
        Eve
      </a>
      <img src="cdn.com/bob.jpg">
    </li>
    <li>
      <a href="/eve">
        Alice
      </a>
      <img src="cdn.com/eve.jpg">
    </li>
  </ul>

这里只有文本节点被重新排列以匹配新的顺序(通过控制台记录阵列顺序来确认),但是链接和图像元素仍然停留在静态构建中的位置。现在,名称,图像和链接都变得混乱了。

另外两件事要注意:

  1. gatsby develop上一切正常。我猜是因为在开发过程中index.html是在主体中没有静态内容的情况下生成的-允许从一开始就对DOM做出完全的控制 静态脚手架以使其混淆。
  2. 使用反应检查器,我看到虚拟DOM和真实DOM不同步。 React认为它已经正确地改组了列表项。检查器显示如下内容:

可读性很强

<ul>
    <li key="165e2405">
        <GatsbyLink to="/bob">
            Bob
        </GatsbyLink>
        <img src="cdn.com/bob.jpg"></img>
    </li>
    <li key="067f9afc">
        <GatsbyLink to="/eve">
            Eve
        </GatsbyLink>
        <img src="cdn.com/eve.jpg"></img>
    </li>
    <li key="ca4b82bf">
        <GatsbyLink to="/alice">
            Alice
        </GatsbyLink>
        <img src="cdn.com/alice.jpg"></img>
    </li>
</ul>

我的问题

  1. 这只是一种非盖茨比式的方法吗? This description of a "Hybrid app page"似乎暗示您可以具有静态或动态组件。我想同时尝试两种方式:我想要 在构建过程中通过graphql从内容获取的配置文件,因此可以通过静态HTML +预先构建的json数据文件(例如/static/d/556/path---index-6a9-L7r5Sntxcv3RUIoHYIR3Qqm9Jmg.json)使用,但 那么我想动态地改组数据并在渲染时重新排列DOM。用盖茨比不可能吗?我是否需要在构建过程中放弃预取的数据,而只考虑一个动态组件并获取 通过componentDidMount中的Contentful API获取数据?
  2. 如果这种方法应该可以,那么我在做什么错了?
  3. 如果这种方法不是偶发的,那么是否有一种方法可以在构建时修改(混洗)通过graphql查询的数据?如果数据仅在构建时重新整理而不在浏览器中在运行时重新重新整理,我实际上会更开心-我可以使站点每隔一个小时左右重新构建一次,站点可能会变得更加静态,从而使客户端更加静态。

1 个答案:

答案 0 :(得分:0)

我最近也在为此苦苦挣扎!

我的解决方案是,一旦使用ReactDOM的render方法挂载父组件,就呈现混洗后的内容:

import React, { useRef, useEffect } from "react";
import { render } from "react-dom";
import shuffle from "../utils/shuffle";

const shuffledArray = shuffle(array.slice());

// The below should still be able to work with graphql fetched data
// as I think the array will be saved to a variable for use in the client,
// although in my case I haven't used it so can't be fully sure
const ShuffledJSXElements = () =>
    shuffledArray.map(creator => (
        <li key={creator.id}>
            <Link to={link}>
                  {creator.name}
            </Link>
            <img src={image} />
      </li>
    ));

const Page = () => {
    const shuffledContentContainerRef = useRef();

    useEffect(() => {
        const ontainer = portfolioContainerRef.current;
        render(<ShuffledJSXElements />, container);
    }, []);

    return (
        <MainWrapper>
            <StyledPortfolioGridWrapper ref={shuffledContentContainerRef} />
        </MainWrapper>
    );
};

export default Portfolio;

一个令人沮丧的事情是容器元素在呈现内容之前不会意识到其自身的高度,因此布局可能会跳一些。一种解决方法是使用min-height css属性。