1

I have a component, menu.js, that i import into a page to produce a list of articles, that can be filtered by category. This works perfectly.

Now i want to change the component so that i can filter the articles by tags. The problem is that the tags are a nested array in graphql, that i cant reach with the same map() function that maps the categories.

I have tried to do a nested map function but i cant get it to work, but i suspect that is the solution. My goal is to have the same functionality where i can filter the articles by tags, instead of by category. I hope thats possible. I am using gatsby, with a Strapi backend. Any hints in the right direction appreciated :-)

/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
          }
        }
      }
    }
  }
`

This is what i get back from the GraphQL query above, each article can of course have one or more tags, but only one category assigned

{
  "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"
              }
            ]
          }
        }
      ]
    }
  }
}

And here is my component that displays the articles according to the chosen category

/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 Answer 1

1

You should be able to use something similar to your category method:

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

Since this isn't necessarily React / Gatsby specific, here is only the data and these methods:

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);

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.