Исправлен рендеринг после обновления Гэтсби и зависимостей

Привет, любитель Гэтсби!

Я пропущу пух и сразу перейду к делу.

Несколько дней назад я обновил зависимости на веб-сайте Gatsby после того, как около года не трогал его. Итак, вы знаете, что это значит, верно?

Ужасный, ужасный хаос. Все сломалось, но большинство ошибок было относительно легко исправить после прочтения текстов ошибок.

Теперь больше всего головной боли мне доставил рендеринг изображений в компоненте Rich Text, запрошенный из Contentful с помощью GraphQL.

Мой старый код выглядел примерно так:

export const pageQuery = graphql`
  query ContentfulBlogPost($slug: String!) {
    contentfulPost(slug: { eq: $slug }) {
      id
      title
      subtitle
      slug
      date(formatString: "Do MMMM, YYYY")
      excerpt
      content {
        json
      }
      description {
        description
        childMarkdownRemark {
          timeToRead
          html
          rawMarkdownBody
        }
      }
    }
    site {
      siteMetadata {
        title
        siteUrl
      }
    }
  }
`;

Компоненты Gatsby, использующие данные, выглядели следующим образом — я удалил нерелевантный код, чтобы за ним было легче следить:

class BlogPostTemplate extends React.Component {
  render() {
    const post = this.props.data.contentfulPost;
    const siteTitle = this.props.data.site.siteMetadata.title;
    const blogContent = this.props.data.contentfulPost.content
      .json;
    const title = post.title;
    const image = post.featuredImage.fluid.src;
    const location = this.props.location;
const Bold = ({ children }) => <b>{children}</b>;
    const Italic = ({ children }) => <i>{children}</i>;
const options = {
      renderMark: {
        [MARKS.BOLD]: (text) => <Bold>{text}</Bold>,
        [MARKS.ITALIC]: (text) => <Italic>{text}</Italic>,
      },
      renderNode: {
        [BLOCKS.PARAGRAPH]: (node, children) => {
          return <TextMedium textMedium={children} />;
        },
        [BLOCKS.HEADING_2]: (node, children) => {
          return <Subtitle subtitle={children} />;
        },
        [BLOCKS.HEADING_3]: (node, children) => {
          return <Heading heading={children} />;
        },
        [BLOCKS.EMBEDDED_ASSET]: (node) => {
          const alt = node.data.target.fields.title["en-US"];
          const url = node.data.target.fields.file["en-US"].url;
          return <img alt={alt} src={url} />;
        },
        [INLINES.HYPERLINK]: (node, children) => {
          return (
            <a className="link" href={node.data.uri}>
              {node.content[0].value}
            </a>
          );
        },
      },
      blockquote: (node) => {
        return (
          <blockquote className="offer">
            {node.content}
          </blockquote>
        );
      },
      hyperlink: (node) => {
        const altText = node.content[0].value;
        const url = node.data.uri;
        return (
          <a className="link" href={url}>
            {altText}
          </a>
        );
      },
    };
return (
      <Layout location={location} title={siteTitle}>
        <SEO
          title={title}
          description={post.excerpt}
          pathname={location.pathname}
          featuredImage={image}
        />
        <Row>
          <Breadcrumb>
            <li>
              <a href="/blog/">Blog home</a>
            </li>
            <li>
              <a href={`${post.slug}`}>{title}</a>
            </li>
          </Breadcrumb>
        </Row>
        <Section isCentered isNarrow>
          <Row isCentered wrap>
            <Title title={title} isCentered />
            <SubHeading subheading={post.subtitle} isCentered />
          </Row>
          <Row isCentered>
            <Column100>
              <BlogBody>
                {documentToReactComponents(blogContent, options)}
              </BlogBody>
              <ReturnLink
                isCentered
                anchortext="Return to all articles"
                href="/blog/"
              />
            </Column100>
          </Row>
          <Subscribe />
        </Section>
      </Layout>
    );
  }
}
export default BlogPostTemplate;

Компонент Gatsby, использующий компонент Rich Text от Contentful, назывался ‹BlogBody› со следующим кодом:

<BlogBody>
   {documentToReactComponents(blogContent, options)}
</BlogBody>

Что больше не работало: блок embedded_asset. Я показываю здесь оба варианта, хотя это те же самые данные, которые я запрашиваю.

[BLOCKS.EMBEDDED_ASSET]: (node) => {
          const alt = node.data.target.fields.title["en-US"];
          const url = node.data.target.fields.file["en-US"].url;
          return <img alt={alt} src={url} />;
        },
OR:
"embedded-asset-block": (node) => {
          const alt = node.data.target.fields.title["en-US"];
          const url = node.data.target.fields.file["en-US"].url;
          return <img alt={alt} src={url} />;
        },

Рефакторинговая версия — сначала запрошенные данные:

export const pageQuery = graphql`
  query ContentfulBlogPost($slug: String!) {
    contentfulPost(slug: { eq: $slug }) {
      id
      title
      subtitle
      slug
      date(formatString: "Do MMMM, YYYY")
      excerpt
      content {
        raw
        references {
          ... on ContentfulAsset {
            __typename
            contentful_id
            id
            title
            file {
              url
            }
          }
          gatsbyImageData(width: 400)
        }
      }
      description {
        childMarkdownRemark {
          timeToRead
          html
          rawMarkdownBody
        }
      }
    }
    site {
      siteMetadata {
        title
        siteUrl
      }
    }
  }
`;

Важным изменением здесь является то, что данные content { json } были заменены на:

content {
        raw
        references {
          ... on ContentfulAsset {
            __typename
            contentful_id
            id
            title
            file {
              url
            }
          }
          gatsbyImageData(width: 400)
        }
      }

Новый компонент Gatsby выглядит так — блок embedded_asset:

[BLOCKS.EMBEDDED_ASSET]: node => {
          const url = node.data.target.file.url
          const alt = node.data.target.title
          return <img alt={alt} src={url} />
        },

И компонент <BlogBody>:

<BlogBody>
   {blogContent?.raw && renderRichText(blogContent, options)}
</BlogBody>

Вот и все, теперь работает :)

Я забыл сохранить фактический текст ошибки, поэтому, если вы получили эту уродливую ошибку и нашли эту статью после нескольких часов, дней и недель гугления, добавьте текст ошибки в комментарий ниже.

Дополнительные материалы на PlainEnglish.io. Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Подпишитесь на нас в Twitter и LinkedIn. Посетите наш Community Discord и присоединитесь к нашему Коллективу талантов.