Filter through posts via OnClick function

Viewed 180

I'm trying to filter through some posts based on their category if a button is clicked. For example I have a button that when clicked the only posts that show up are related to software projects.

I have set up a function called searchHandler that I've passed through to my SidebarOptions component, which has the onclick event. But when I pass it through nothing happens.

Here is the code in the (parent) Home Component where the searchHandler is:

function Home() {
  const [posts, setPosts] = useState([]);
  const [filteredPosts, setFilteredPosts] = useState(null);

  const searchHandler = (event) => {
    const { value } = event.target;
    setFilteredPosts(
      value
        ? posts.filter(
            (post) =>
              post.question.question.includes(value)
          )
        : null
    );
  };

  useEffect(() => {
    db.collection("questions")
      .orderBy("timestamp", "desc")
      .onSnapshot((snapshot) =>
        setPosts(
          snapshot.docs.map((doc) => ({
            id: doc.id,
            question: doc.data(),
          }))
        )
      );
  }, []);

  return (
    <div className="home">
      <div></div>
      <Header searchHandler={searchHandler} />
      <div className="home__content">
        <Sidebar searchHandler={searchHandler} />
        <Feed posts={filteredPosts || posts} />
        <Widget />
      </div>
    </div>
  );
}

Here is the (child) Sidebar component that receives it:

import React from "react";
import "../Style/Sidebar.css";
import SidebarOptions from "./SidebarOptions";

function Sidebar({ searchHandler }) {
  return (
    <div className="sidebar">
      <SidebarOptions searchHandler={searchHandler} />
    </div>
  );
}

export default Sidebar;

And here is the (grandchild)SidebarOptions that the function is finally sent to:

function SidebarOptions({ searchHandler }) {

  return (
    <div className="sidebarOptions">
      <div className="sidebarOption" onChange={() => searchHandler}>
        <img
          src="https://c.pxhere.com/photos/7b/1a/code_coding_computer_developer_developing_development_macbook_notebook-913320.jpg!d"
          srcset="https://c.pxhere.com/photos/7b/1a/code_coding_computer_developer_developing_development_macbook_notebook-913320.jpg!d"
          alt="Software Projects"
        />
        <p>Software Projects</p>
      </div>
 );
};
2 Answers

I think you need to revisit your SideBarOptions component. I wonder if the onChange handler makes sense on a div. I think it should be input rather than a div if you want your user to type. Also, you need to call your handler with the value that is typed, here you are not calling the handler (notice the missing () after searchHandler in your code for SideBarOptions). Also, it will be better to add something like a debounce so that the filter is not triggered for every character that a user types. It should ideally be triggered once a user stops typing, debounce is precisely that.

Putting some code snippet below based on my guess about how it might work.

const SideBarOptions = ({ searchHandler }) => {
  const [filterText, setFilterText] = useState("");
  
  const handleFilter = () => {
    searchHandler(filterText);
  }

  return (
    <div className="sidebarOptions">
      <input name="filterText" value={filterText} onChange={(e) => setFilterText(e.target.value)} />
      <div className="sidebarOption" onChange={() => searchHandler}>
        <img src="https://c.pxhere.com/photos/7b/1a/code_coding_computer_developer_developing_development_macbook_notebook-913320.jpg!d" srcset="https://c.pxhere.com/photos/7b/1a/code_coding_computer_developer_developing_development_macbook_notebook-913320.jpg!d"
          alt="Software Projects"
        />
        <p>Software Projects</p>
        <button onClick={handleFilter}>Filter</button>
      </div>
    </div>
  );
}

So I was able to solve this by making a new function called categoryfilter in the Home component that went through the options and looked for the category of the posts in the database:

const categoryFilter = (category = "All") => {
    const filtered =
        category === "All"
            ? posts
            : posts.filter(({ question }) => question.option === category);
    setFilteredPosts(filtered);
};

I then passed that code as a prop to the sidebarOptions div after cleaning up the code a bit and used it to filter the posts based on the category name:

function SidebarOptions({ categoryFilter }) {
    const categories = [
        //Add all projects
        {
            name: "All",
            imgUrl: "",
        },
        {
            name: "Software Project",
            imgUrl:
                "https://c.pxhere.com/photos/7b/1a/code_coding_computer_developer_developing_development_macbook_notebook-913320.jpg!d",
        },
        {
            name: "Engineering Project",
            imgUrl:
                "https://c.pxhere.com/photos/a7/72/gears_cogs_machine_machinery_mechanical_printing_press_gears_and_cogs_technology-818429.jpg!d",
        },
    ];

    return (
        <div className="sidebarOptions">
            {categories.map((category) => (
                <div
                    className="sidebarOption"
                    onClick={() => categoryFilter(category.name)}
                >
                    {category.imgUrl && (
                        <img
                            src={category.imgUrl}
                            srcSet={category.imgUrl}
                            alt={category.name}
                        />
                    )}

                    <p>{category.name}</p>
                </div>
            ))}
            <div className="sidebarOption">
                <Add />
                <p>Suggest Project Space</p>
            </div>
        </div>
    );
}

export default SidebarOptions;
Related