如何使用 reactjs 和 graphql 在 gatsby 中映射嵌套数组

Posted

技术标签:

【中文标题】如何使用 reactjs 和 graphql 在 gatsby 中映射嵌套数组【英文标题】:How to map nested array in gatsby with reactjs and graphql 【发布时间】:2020-01-16 07:48:42 【问题描述】:

我有一个组件 menu.js,我将其导入页面以生成可以按类别过滤的文章列表。这完美无缺。

现在我想更改组件,以便我可以按标签过滤文章。问题是标签是 graphql 中的嵌套数组,我无法使用映射类别的相同 map() 函数来访问它。

我试图做一个嵌套的地图功能,但我不能让它工作,但我怀疑这是解决方案。我的目标是拥有相同的功能,我可以按标签而不是按类别过滤文章。我希望那是可能的。我正在使用带有 Strapi 后端的 gatsby。任何正确方向的提示表示赞赏:-)

/src/pages/articles.js

import graphql from 'gatsby'
import React from 'react'
import Layout from 'components/layout'
import MenuBlog from 'components/menublog'

const BlogPage = ( data ) => (
  <Layout>
      <MenuBlog items=data.menu />
  </Layout>
)

export default BlogPage

export const pageQuery = graphql`
  query BlogQuery 
    menu: allStrapiArticle 
      edges 
        node 
          id
          title
          slug
          tag 
            title
            id
          
          category 
            title
            id
          
        
      
    
  
`

这是我从上面的 GraphQL 查询中得到的,每篇文章当然可以有一个或多个标签,但只能分配一个类别


  "data": 
    "menu": 
      "edges": [
        
          "node": 
            "title": "articleName 1",
            "slug": "articleName-1",
            "category": 
              "title": "cat1"
            ,
            "tag": [
              
                "title": "tag1"
              ,
              
                "title": "tag2"
              ,
              
                "title": "tag3"
              
            ]
          
        ,
        
          "node": 
            "title": "articleName 2",
            "slug": "articleName-2",
            "category": 
              "title": "cat2"
            ,
            "tag": [
              
                "title": "tag3"
              
            ]
          
        
      ]
    
  

这是我的组件,根据所选类别显示文章

/src/components/menublog/index.js

import React,  Component  from 'react'
import  Link  from 'gatsby'
import Row from 'react-bootstrap/Row'

const getCategories = items => 
  let tempItems = items.map(items => 
    return items.node.category.title
  )
  let tempCategories = new Set(tempItems)
  let categories = Array.from(tempCategories)
  categories = ['all', ...categories]
  return categories


export default class MenuBlog extends Component 
  constructor(props) 
    super(props)
    this.state = 
      items: props.items.edges,
      articles: props.items.edges,
      categories: getCategories(props.items.edges),
    
  

  handleItems = category => 
    let tempItems = [...this.state.items]
    if (category === 'all') 
      this.setState(() => 
        return  articles: tempItems 
      )
     else 
      let items = tempItems.filter(
        ( node ) => node.category.title === category
      )
      this.setState(() => 
        return  articles: items 
      )
    
  
  render() 
    if (this.state.items.length > 0) 
      return (
        <Row>
          /* items */
          <div className="col-md-8 blog-main bg-light">
            <h1>Artikler</h1>
            this.state.articles.map(( node ) => 
              return (
                <div key=node.id className="blog-post mb-4">
                  <h2>
                    <Link to=`/artikler/$node.slug`>node.title</Link>
                  </h2>
                  /* item text */
                </div>
              )
            )
          </div>
          /* categories */
          <div className="col-md-4 blog-sidebar">
            <div className="p-4 mb-3 bg-light">
              <h4>Kategorier</h4>
              <ol className="list-unstyled mb-0">
                this.state.categories.map((category, index) => 
                  return (
                    <li key=index>
                      <button
                        type="button"
                        className="btn"
                        onClick=() => 
                          this.handleItems(category)
                        
                      >
                        category
                      </button>
                    </li>
                  )
                )
              </ol>
            </div>
            <div className="p-4 mb-3 bg-light">
              <h4>Kategorier</h4>
            </div>
          </div>
        </Row>
      )
     else 
      return <h1>no items</h1>
    
  

【问题讨论】:

【参考方案1】:

您应该能够使用类似于您的类别方法的东西:

items = tempItems.filter(( node ) =>
  node.tag.map(tag => tag.title).includes("tag2")
);

由于这不一定是 React / Gatsby 特定的,这里只有数据和这些方法:

const data = 
  data: 
    menu: 
      edges: [
          node: 
            title: "articleName 1",
            slug: "articleName-1",
            category: 
              title: "cat1"
            ,
            tag: [
                title: "tag1"
              ,
              
                title: "tag2"
              ,
              
                title: "tag3"
              
            ]
          
        ,
        
          node: 
            title: "articleName 2",
            slug: "articleName-2",
            category: 
              title: "cat2"
            ,
            tag: [
              title: "tag3"
            ]
          
        
      ]
    
  
;

let items = data.data.menu.edges.filter(
  (
    node
  ) => node.category.title === "cat2"
);

console.log(items);

items = data.data.menu.edges.filter((
    node
  ) =>
  node.tag.map(tag => tag.title).includes("tag2")
);

console.log(items);

【讨论】:

以上是关于如何使用 reactjs 和 graphql 在 gatsby 中映射嵌套数组的主要内容,如果未能解决你的问题,请参考以下文章

如何在 reactJS 中映射嵌套的 graphQL 查询

如何在 apollo graphql reactjs 中使用 detalization 查询?

GraphQL 和 ReactJS 状态

在 reactJS 中使用动态字段的 Apollo 客户端 graphql 查询

使用 GraphQL 和 ReactJS 未定义导入的 <Query> 组件

ReactJS/Javascript/Graphql/React-apollo:传递查询变量