How to render it using react js

Viewed 43

Currently, I'm able to render the posts on screen but how can I render using the select dropdown where the user can select different sort options like title, author, and description, and the posts get sorted accordingly? What should I change in order for it works any suggestions will be much appreciated. I have added both HomePage.js and Post.js which I'm using.

//HomePage.js

import React, { useState, useEffect, useMemo } from "react";
        import Post from "../../Components/Post/Post";
        import "./HomePage.css";
        import axios from "axios";
        
        const HomePage = () => {
          const [posts, setPosts] = useState("");
        
          let config = { Authorization: "..................." };
          const url = "........................";
        
          useEffect(() => {
            AllPosts();
          }, []);
        
          const AllPosts = () => {
            axios
              .get(`${url}`, { headers: config })
        
              .then((response) => {
                const allPosts = response.data.articles;
                setPosts(allPosts);
                console.log(response);
              })
              .catch((error) => console.error(`Error: ${error}`));
          };
        
          const newPostsByTitle = useMemo(() => {
            return [...posts].sort((a, b) => a.title.localeCompare(b.title));
          }, [posts]);
        
          console.log("sorted by title", newPostsByTitle);
        
          const newPostsByAuthor = useMemo(() => {
            return [...posts].sort((a, b) => a.author.localeCompare(b.author));
          }, [posts]);
        
          console.log("sorted by author", newPostsByAuthor);
        
          const newPostsByDescription = useMemo(() => {
            return [...posts].sort((a, b) =>
              a.description.localeCompare(b.description)
            );
          }, [posts]);
        
          console.log("sorted by description", newPostsByDescription);
        
          return (
            <div className="home">
              <div className="select">
                <select
                  name="slct"
                  id="slct"
                  onChange={(e) => AllPosts(e.target.value)}
                >
                  <option value="Title">Title</option>
                  <option value="Author">Author</option>
                  <option value="Description">Description</option>
                </select>
              </div>
              <Post className="Posts" posts={posts} key={posts.title} />
            </div>
          );
        };
        
        export default HomePage;

//Post.js

import React from "react";
import "./Post.css";
import { Fragment } from "react";

const Post = (props) => {
  const displayPosts = (props) => {
    const { posts } = props;

    if (posts.length > 0) {
      return posts.map((post) => {
        return (
          <Fragment>
            <div className="Post" key={post.title}>
              <img
                src={post.urlToImage}
                alt="covid"
                width="100%"
                className="img"
              />
              <h5 className="title"> {post.title}</h5>
              <p className="author"> {post.author}</p>
              <p className="description"> {post.description}</p>
            </div>
          </Fragment>
        );
      });
    }
  };
  return <div className="Posts">{displayPosts(props)}</div>;
};

export default Post;
1 Answers

Issue

You don't actually call newPostsByTitle, newPostsByAuthor, or newPostsByDescription anywhere and capture the returned sorted posts array.

Suggestion

I suggest storing the selected sort value in state and doing an inline sort of the posts state when passing it to the child component.

const HomePage = () => {
  const [posts, setPosts] = useState("");
  const [sortBy, setSortBy] = useState("none"); // <-- add sortBy state

  ...

  useEffect(() => {
    AllPosts();
  }, []);

  const AllPosts = () => {
    ...
  };

  return (
    <div className="home">
      <div className="select">
        <select
          name="slct"
          id="slct"
          onChange={(e) => setSortBy(e.target.value)}
        >
          <option value="title">Title</option> // <-- lowercase value to match property
          <option value="author">Author</option>
          <option value="description">Description</option>
          <option value="none">No Sort</option> // <-- add no sort option
        </select>
      </div>
      <Posts
        className="Posts"
        posts={
          sortBy === "none" // <-- conditionally sort by property
            ? posts
            : posts.sort((a, b) => a[sortBy].localeCompare(b[sortBy]))
        }
        key={posts.title}
      />
    </div>
  );
};

If you don't need or want the "no sort" option then just provide one of "title", "name", or "description" as the initial sortBy state value and forego the ternary check and just do the sort.

Update

Sounds like you need to use the useMemo hook by some external requirement. Basically move the ternary logic into the hook with a dependency on sortBy state and pass the memoized value to the Posts component's posts prop.

const sortedPosts = useMemo(() => {
  return sortBy === "none"
    ? posts
    : posts.sort((a, b) => a[sortBy].localeCompare(b[sortBy]))
}, [sortBy]);

...

<Posts
  className="Posts"
  posts={sortedPosts}
  key={posts.title}
/>
Related