You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React+Gatsby+Contentful项目中富文本列表重复显示问题求助

问题:React/Gatsby + Contentful 文章列表中Rich Text字段重复显示,无法对应单篇文章摘要

问题描述

我在基于React和Gatsby的项目中使用Contentful作为内容源,Content模型里大部分是普通字符串字段,只有最后一个是Rich Text类型。我想实现文章列表卡片,展示标题、发布日期和简短摘要,所以重置了Rich Text的格式让它像普通文本。但渲染列表时,普通字段显示正常,Rich Text字段却在每个卡片里重复显示,没法对应每篇文章的唯一摘要。我试了几种方法都没解决,以下是页面代码:

import React from "react"
import { graphql } from 'gatsby'
import { BLOCKS, MARKS } from "@contentful/rich-text-types"
import { renderRichText } from "gatsby-source-contentful/rich-text"
import Layout from "../components/base/layout"
import { PageWrapper, BlogCardWrapper, BlogCardElement, ThumbnailImage, ContentInlineWrapper, PreContentParagraph, ReadMoreParagraph, BlogTitleHeader, BlogDateParagraph, } from "../styles/aktualnosci.style"
import { Headers } from "../utils/data/headersData"
import H1 from "../components/headers/h1"

const AktualnosciPage = ({ data }) => {
 const articles = data.allContentfulArtykul.edges
 const post = articles[0].node.content
 const option = {
 renderNode: {
 [BLOCKS.EMBEDDED_ASSET]: node => {
 return <img/>
 },
 [BLOCKS.HEADING_1]: (node, children) => {
 return <p style={{padding: '0', margin: '0', display: 'inline-block'}}>{children}</p>
 },
 [BLOCKS.HEADING_5]: (node, children) => {
 return <p style={{padding: '0', margin: '0', display: 'inline-block'}}>{children}</p>
 },
 [BLOCKS.PARAGRAPH]: (node, children) => {
 return <p style={{padding: '0', margin: '0', display: 'inline-block'}}>{children}</p>
 },
 [BLOCKS.QUOTE]: (node, children) => {
 return <p style={{padding: '0', margin: '0', display: 'inline-block'}}>{children}</p>
 },
 [BLOCKS.UL_LIST]: (node, children) => {
 return <p style={{padding: '0', margin: '0', display: 'inline-block'}}>{children}</p>
 },
 [BLOCKS.LIST_ITEM]: (node, children) => {
 return <p style={{padding: '0', margin: '0', display: 'inline-block'}}>{children}</p>
 },
 },
 renderMark: {
 [MARKS.BOLD]: (node, children) => {
 return <p style={{fontWeight: '100'}}>{children}</p>
 },
 }
 }
 const output = renderRichText(post, option)
 return (
 <Layout>
 <PageWrapper>
 <H1 name={ Headers.Aktualnosci }/>
 <BlogCardWrapper>
 {articles.reverse().map(({node}) => {
 return (
 <div key={node.slug}>
 <a href={"/aktualnosci/" + node.slug}>
 <BlogCardElement>
 <ThumbnailImage className="j10_dfg4gvBDG" src={node.thumbnailPhoto.fluid.src} srcSet={node.thumbnailPhoto.fluid.srcSet} />
 <ContentInlineWrapper>
 <BlogTitleHeader>{node.title}</BlogTitleHeader>
 <PreContentParagraph>{output}</PreContentParagraph>
 <BlogDateParagraph>{node.createdAt}</BlogDateParagraph>
 </ContentInlineWrapper>
 <ReadMoreParagraph className="j5_dfg4gvBDG">Czytaj więcej <span style={{color: '#BF1E2D', fontSize: '11px'}}>&#10148;</span></ReadMoreParagraph>
 </BlogCardElement>
 </a>
 </div>
 )
 })}
 </BlogCardWrapper>
 </PageWrapper>
 </Layout>
 )
}

export const query = graphql`
 query {
 allContentfulArtykul {
 edges {
 node {
 id
 thumbnailPhoto {
 fluid {
 src
 srcSet
 }
 }
 title
 slug
 content {
 raw
 references {
 ... on ContentfulAsset {
 __typename
 contentful_id
 fixed(width: 200) {
 src
 srcSet
 }
 }
 }
 }
 createdAt(formatString: "YYYY-MM-DD")
 }
 }
 }
 }
`

export default AktualnosciPage

解决方案

问题出在你提前只渲染了第一篇文章的Rich Text内容(const post = articles[0].node.content),然后在循环所有文章时,每个卡片都复用了这个output变量——相当于所有卡片都在显示第一篇文章的摘要,自然会重复。

你需要把Rich Text的渲染逻辑放到循环内部,让每篇文章都用自己的node.content来生成对应的摘要。具体修改如下:

  1. 保留option配置(可以放在组件内部任意位置,不影响)
  2. 删除全局的post和output变量
  3. 在map循环中,对当前遍历到的node的content调用renderRichText

修改后的核心代码片段:

const AktualnosciPage = ({ data }) => {
 const articles = data.allContentfulArtykul.edges
 // 保留原有的Rich Text渲染配置
 const option = {
   renderNode: {
     [BLOCKS.EMBEDDED_ASSET]: node => <img/>,
     [BLOCKS.HEADING_1]: (node, children) => <p style={{padding: '0', margin: '0', display: 'inline-block'}}>{children}</p>,
     [BLOCKS.HEADING_5]: (node, children) => <p style={{padding: '0', margin: '0', display: 'inline-block'}}>{children}</p>,
     [BLOCKS.PARAGRAPH]: (node, children) => <p style={{padding: '0', margin: '0', display: 'inline-block'}}>{children}</p>,
     [BLOCKS.QUOTE]: (node, children) => <p style={{padding: '0', margin: '0', display: 'inline-block'}}>{children}</p>,
     [BLOCKS.UL_LIST]: (node, children) => <p style={{padding: '0', margin: '0', display: 'inline-block'}}>{children}</p>,
     [BLOCKS.LIST_ITEM]: (node, children) => <p style={{padding: '0', margin: '0', display: 'inline-block'}}>{children}</p>,
   },
   renderMark: {
     [MARKS.BOLD]: (node, children) => <p style={{fontWeight: '100'}}>{children}</p>,
   }
 }

 return (
   <Layout>
     <PageWrapper>
       <H1 name={ Headers.Aktualnosci }/>
       <BlogCardWrapper>
         {articles.reverse().map(({node}) => {
           // 关键:在循环内为当前文章单独渲染Rich Text
           const postOutput = renderRichText(node.content, option)
           return (
             <div key={node.slug}>
               <a href={"/aktualnosci/" + node.slug}>
                 <BlogCardElement>
                   <ThumbnailImage className="j10_dfg4gvBDG" src={node.thumbnailPhoto.fluid.src} srcSet={node.thumbnailPhoto.fluid.srcSet} />
                   <ContentInlineWrapper>
                     <BlogTitleHeader>{node.title}</BlogTitleHeader>
                     {/* 使用当前文章的渲染结果 */}
                     <PreContentParagraph>{postOutput}</PreContentParagraph>
                     <BlogDateParagraph>{node.createdAt}</BlogDateParagraph>
                   </ContentInlineWrapper>
                   <ReadMoreParagraph className="j5_dfg4gvBDG">Czytaj więcej <span style={{color: '#BF1E2D', fontSize: '11px'}}>&#10148;</span></ReadMoreParagraph>
                 </BlogCardElement>
               </a>
             </div>
           )
         })}
       </BlogCardWrapper>
     </PageWrapper>
   </Layout>
 )
}

这样每篇文章都会渲染自己的Rich Text内容,就不会出现重复的问题了。另外,如果你需要控制摘要长度,建议在Contentful中单独添加一个“摘要”字符串字段,这样比处理整个Rich Text内容更高效,也能避免格式处理的麻烦。

内容的提问来源于stack exchange,提问作者Raczek

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.01 03:27:43