Home > database >  React button not rerendering the component
React button not rerendering the component

Time:12-28

I am making a blog with React and Sanity, and am trying to use buttons to allow the user to sort blog posts by tag (category). Currently, the sorting works fine if I hard code the tag I want to sort the posts by, but I want to be able to use buttons to change the sort tag. Right now the button is able to change text on the screen to the correct category, but It does not update the visible posts (does not actually sort them).

Here is my code for the blog page:

import React from "react";
import { useState, useEffect } from "react"

import Footer from "../Footer";
import Header from "./Header";

import client from "../../client"
import BlockContent from "@sanity/block-content-to-react";

import { Link } from "react-router-dom";

const Blog = () =>
{
    const [posts, setPosts] = useState([])
    const [sort, setSort] = useState("all")
    
    useEffect(() => {
        client.fetch(
            //This line sorts posts by the value of 'keyword' 
            `*[_type == "post" && $keyword in tags] { 
                title,
                slug,
                body,
                mainImage {
                    asset -> {
                        _id,
                        url
                    },
                    alt
                },
                tags,
                publishedAt
            }`,{"keyword":sort} //This line sets the value of 'keyword' to the value of 'sort'
        )
        .then((data) => setPosts(data))
        .catch(console.error)
    }, [])

    //This function prints the tag that was clicked to the console and updates the 
    //value of 'sort'. It is supposed to rerender the component to display only the
    //posts associated with the tag clicked, but it does not.
    function sortPosts(e) {
        console.log(e)
        setSort(e)
    }

    return (
        <div className = "bg-gray-100 dark:bg-zinc-900">
            <Header />
            <div className = "" >
                <p className = "title pt-32">Welcome to My Blog!</p>

                //This line correctly displays the tag that is supposed to determine which posts to display
                <p className = "text-center font-semibold">Currently showing: {sort} posts</p>

                <div className = "flex items-center justify-center mt-16">
                    <div className = "grid grid-cols-1 xl:grid-cols-3 lg:grid-cols-2 md:grid-cols-1 gap-10 mx-16">
                    {posts.map((post) => (
                        <article key={post.slug.current} className = "rounded-xl max-w-sm bg-white dark:bg-zinc-950 shadow-xl dark:shadow-gray-100/10">
                            <img src={post.mainImage.asset.url} alt={post.title} className = "object-fill object-center rounded-t-xl" />
                            <div className = "p-6">
                                <p className = "text-2xl font-semibold mb-3 dark:text-gray-100">{post.title}</p>
                                {post.tags.map((tags, key) => (
                                    <div className = "inline-block">

                                        //This line displays a button for each tag associated with the current post and calls the sortPosts function
                                        <button key = {key} onClick={() => sortPosts(tags)} className = "px-2 inline-flex mb-2 mr-2 rounded-2xl hover:bg-white bg-gray-100 dark:hover:bg-zinc-950 dark:bg-zinc-800 dark:text-white duration-300 transition-colors cursor-pointer">{tags}</button>

                                    </div>
                                ))}
                                <div class = "preview">
                                    <BlockContent blocks={post.body} projectId="2hp9gld0" dataset="production" />
                                </div>
                                <button className="button-main items-center mt-2 dark:text-gray-100 block">
                                    <Link to = {`/blog/${post.slug.current}`} className = "">Read Full Article</Link>
                                </button>
                            </div>
                        </article>
                    ))}
                    </div>
                </div>
            </div>
            <div className = "pb-10 bg-gray-100 dark:bg-zinc-900">
                <Footer />
            </div>
        </div>
    )
}

export default Blog;

As I said, this code works perfectly fine other than actually rerendering the correct posts when a button is clicked to sort by a specific tag. Any help is appreciated!

CodePudding user response:

If you fetch data and setPosts in useEffect, You should add sort state to useEffect dependency, to update the posts when you change the sort state value by setSort. like below:

    const [posts, setPosts] = useState([])
    const [sort, setSort] = useState("all")
    
    useEffect(() => {
        client.fetch(
            //This line sorts posts by the value of 'keyword' 
            ...
        .then((data) => setPosts(data))
        .catch(console.error)
    }, [sort])
  • Related