reactjstailwind-csssanitygroq

Sanity posts display all categories, not just ones associated with post


I am working on making a blog using React and Sanity, and I want to display the categories associated with each post underneath each post. I am able to display the categories, but it displays all categories, not just the ones associated with each specific post. How can I make it so each posts displays only the categories associated with it?

Here is my code:

import React, { useEffect, useState } from 'react'
import client from '../client'
import BlockContent from '@sanity/block-content-to-react'
import { Link } from 'react-router-dom'

function Main() {
    const [posts, setPosts] = useState([])

    useEffect(() => {
        client.fetch(
            `*[_type == "post"] {
                title,
                slug,
                body,
                author,
                mainImage {
                    asset -> {
                        _id,
                        url
                    },
                    alt
                },
                publishedAt,
                "categories": categories[]->title
            }`
        ).then((data) => setPosts(data))
         .catch(console.error)
    }, [])

    return (
        <div className='grid lg:grid-cols-3 md:grid-cols-2 gap-8 m-4 '>
            <div>
                {posts.slice(0, 1).map((p, i) => (
                    <Link to = {`/blog/${p.slug.current}`} className=''>
                        <article key = {p.slug.current} className=''>
                            <img src = {p.mainImage.asset.url} alt = {p.title} className='' />
                            <div>
                                <p className='font-bold text-xl text-secondary'>{p.title}</p>
                                <div className=''>
                                    <p className='text-sm'>By Brandon Pyle | {new Date(p.publishedAt).toLocaleDateString()}</p>
                                    {posts.map((c, i) => (
                                        <p className='inline'>{c.categories}, </p>
                                    ))}
                                </div>
                            </div>
                        </article>
                    </Link>
                ))}
            </div>

            <div className='my-[-16px]'>
                {posts.slice(1, 4).map((p, i) => (
                    <Link to = {`/blog/${p.slug.current}`} className='col-start-2'>
                        <article key = {p.slug.current} className='flex my-4'>
                            <img src = {p.mainImage.asset.url} alt = {p.title} className='w-auto h-auto max-h-[80px]' />
                            <div>
                                <p className='font-bold text-xl text-secondary'>{p.title}</p>
                                <p className='text-sm'>By Brandon Pyle | {new Date(p.publishedAt).toLocaleDateString()}</p>
                                <div>
                                    {posts.map((c, i) => (
                                        <p className='inline'>{c.categories}, </p>
                                    ))}
                                </div>
                            </div>
                        </article>
                    </Link>
                ))}
            </div>
        </div>
    )
}

export default Main

Solution

  • Thanks to @cfm I was able to find a solution:

    Your query looks good. But you are mapping posts again inside the .map of posts. I would expect you to do p.categories.map in the inner map, if you wanted only this posts categories titles.

    As he suggested, I simply switched posts.map to p.categories.map, and this fixed the problem! Here is the full fixed code:

    import React, { useEffect, useState } from 'react'
    import client from '../client'
    import BlockContent from '@sanity/block-content-to-react'
    import { Link } from 'react-router-dom'
    
    function Main() {
        const [posts, setPosts] = useState([])
    
        useEffect(() => {
            client.fetch(
                `*[_type == "post"] {
                    title,
                    slug,
                    body,
                    author,
                    mainImage {
                        asset -> {
                            _id,
                            url
                        },
                        alt
                    },
                    publishedAt,
                    "categories": categories[]->title
                }`
            ).then((data) => setPosts(data))
             .catch(console.error)
        }, [])
    
        return (
            <div className='grid lg:grid-cols-3 md:grid-cols-2 gap-8 m-4 '>
                <div>
                    {posts.slice(0, 1).map((p, i) => (
                        <Link to = {`/blog/${p.slug.current}`} className=''>
                            <article key = {p.slug.current} className=''>
                                <img src = {p.mainImage.asset.url} alt = {p.title} className='' />
                                <div>
                                    <p className='font-bold text-xl text-secondary'>{p.title}</p>
                                    <div className=''>
                                        <p className='text-sm'>By Brandon Pyle | {new Date(p.publishedAt).toLocaleDateString()}</p>
                                        {p.categories.map((c, i) => (
                                            <p className='inline'>{c}, </p>
                                        ))}
                                    </div>
                                </div>
                            </article>
                        </Link>
                    ))}
                </div>
    
                <div className='my-[-16px]'>
                    {posts.slice(1, 4).map((p, i) => (
                        <Link to = {`/blog/${p.slug.current}`} className='col-start-2'>
                            <article key = {p.slug.current} className='flex my-4'>
                                <img src = {p.mainImage.asset.url} alt = {p.title} className='w-auto h-auto max-h-[80px]' />
                                <div>
                                    <p className='font-bold text-xl text-secondary'>{p.title}</p>
                                    <p className='text-sm'>By Brandon Pyle | {new Date(p.publishedAt).toLocaleDateString()}</p>
                                    <div>
                                        {p.categories.map((c, i) => (
                                            <p className='inline'>{c}, </p>
                                        ))}
                                    </div>
                                </div>
                            </article>
                        </Link>
                    ))}
                </div>
            </div>
        )
    }
    
    export default Main