0

The problem is - (trying to solve to problem of clicking on one article from a list of articles to then direct the user to that particular article to read) need to render a new page from an API call using the slug. The AllPosts page lists different articles. It requires a clickable link on the image in the list of articles to direct to the OnePost page that has the article on it, using data from the API. I have started on the BlogCard component where I use a Link component that wraps around the Title and Image from the article list. The Link component has the destination as {/posts/${slug}}>. the next part is trying to manage the routing on the App.js page, or if there is a more effective solution to this for the desired result(or If someone chooses a better way to do this). and finally, on the OnePost page (that the article is written on) I have started to make the API call with the intention of making it the same as the AllPost component(using AXIOS with graphQL schema), so far with little success. the OnePost page will display the article title and cover image. I have included a sandbox link to follow. And have left a commented-out code so it's not broken. Where it's at It will display the list of articles on the AllPosts page taken from the API data. Thanks so much in advance, I hope this all makes sense, any help or direction would be much appreciated.............. Thanks again - Python-Pirate 🐍🏴‍☠️😃................... sandbox link -----> https://codesandbox.io/s/2-1-23-react-slug-link-3ncqli?file=/src/components/BlogCard.js

import React from "react";
import { Link } from "react-router-dom";
import "./styles.css";

const BlogCard = ({ coverPhoto, title }) => {
  return (
    <div className="card">
      <Link to={`/posts/${slug}`}>
      <h2>{title}</h2>
      <img
        src={coverPhoto ? coverPhoto.url : null}
        alt=""
        height={200}
        width={200}
      ></img>
      </Link>
    </div>
  );
};
export default BlogCard;

import React from "react";
import { useQuery } from "react-query";
 import axios from "axios";

const endpoint =
   "";

 const QUERY = `
  {
     posts {
       id
       title
       slug
       coverPhoto {
         id
         url
       }
     }
   }
 `;
 const SLUGLIST = `
   {
     posts {
       slug
     }
   }
 `;

const OnePost = ({ post }) => {
  return (
    <div>
      <h1>One Post Page</h1>
      <img
        src={post.coverPhoto ? post.coverPhoto.url : null}
        alt=""
        height={200}
        width={600}
      ></img>
     <h2>{post.title}</h2>
    </div>
  );
};

export default OnePost;

import React from "react";
import { useQuery } from "react-query";
import axios from "axios";
import BlogCard from "../../components/BlogCard";

const endpoint =
  "";

const QUERY = `
{
  posts {
    id
    title
    slug
    coverPhoto {
      createdBy {
        id
      }
      url
    }
  }
}
`;

const AllPosts = () => {
  const { data, isLoading, error } = useQuery("blog_posts", async () => {
    const response = await axios({
      url: endpoint,
      method: "POST",
      data: {
        query: QUERY
      }
    });
    return response.data.data;
  });

  if (isLoading) return "Loading...";
  if (error) return <pre>{error.message}</pre>;

  return (
    <div>
      <h1>Top Web Development Resources 2023</h1>
      <ul>
        {data.posts.map((post) => (
          <BlogCard
            title={post.title}
            key={post.id}
            coverPhoto={post.coverPhoto}
          />
        ))}
      </ul>
    </div>
  );
};

export default AllPosts;

import React from "react";
import { QueryClient, QueryClientProvider } from "react-query";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import AllPosts from "./Pages/AllPosts/AllPosts";
import OnePost from "./Pages/OnePost/OnePost";

const queryClient = new QueryClient();

const App = () => (
  <>
    <Router>
      <Switch>
    <QueryClientProvider client={queryClient}>
      <Route path="/" component={AllPosts} />
          <Route path="/onepost" component={OnePost} />
      <AllPosts />
    </QueryClientProvider>
    </Switch>
    </Router>
  </>
);

export default App;

The 2 images are examples of a list of posts and a single post, using the same data.

This is a example of a list of posts

enter image description here

7
  • What are you trying to fetch in the OnePost component, and how are you trying to fetch it? Commented Jan 2, 2023 at 20:29
  • Hi, I'm trying to fetch the title and cover photo in the OnePost component. I managed to achieve the API call in the AllPost component with Axios to display the list of articles but am having little success in doing this in the OnePost component. 😃 Commented Jan 2, 2023 at 22:07
  • Right, but where in the code are you trying to do this? Do you have any documentation for these api-ap-southeast-2.hygraph.com APIs you are trying to use? It seems your question is basically how to make a specific query. Is the data for the OnePost component different than the data from the AllPosts component? Commented Jan 2, 2023 at 22:12
  • Hi, the data for OnePost and AllPost are the same data. I have documentation about the API here - hygraph.com/docs. Im trying to write the code for the new page that displays the article title and cover photo in OnePost but have only gotten as far as writing the API endpoint, the graphql, and the return statement with the title and cover photo in it. still not sure how to complete this. Commented Jan 2, 2023 at 22:32
  • 1
    No, I mean, the API for your data, what you need to query and how you query it, i.e. the data schema. I am able to refetch all the data again and filter it client-side, but I don't think that's quite what you are asking for. That said, if the data is identical why not just fetch it all once and pass the data in route state to the detail route? Commented Jan 2, 2023 at 22:47

1 Answer 1

1

Since the data between the two pages/components is identical then I'd suggest fetching is once in a parent component and making it available to descendants via a React context.

Create a layout route that fetches the posts and provides it via an Outlet component's context. This is effectively just moving the fetching logic from AllPosts to a new layout route component.

import { QueryClient, QueryClientProvider, useQuery } from "react-query";
import axios from "axios";
import {
  BrowserRouter as Router,
  Routes,
  Route,
  Navigate,
  Outlet
} from "react-router-dom";
import AllPosts from "./Pages/AllPosts/AllPosts";
import OnePost from "./Pages/OnePost/OnePost";

const queryClient = new QueryClient();

const endpoint =
  "https://api-ap-southeast-2.hygraph.com/v2/clcdbkbxr45xk01tcdpxgg3sh/master";

const QUERY = `
{
  posts {
    id
    title
    slug
    coverPhoto {
      createdBy {
        id
      }
      url
    }
  }
}
`;

const PostsLayout = () => {
  const { data, isLoading, error } = useQuery("blog_posts", async () => {
    const response = await axios({
      url: endpoint,
      method: "POST",
      data: {
        query: QUERY
      }
    });
    return response.data.data;
  });

  if (isLoading) return "Loading...";
  if (error) return <pre>{error.message}</pre>;

  return <Outlet context={{ posts: data.posts }} />;
};

Render the layout route to wrap the two existing routes.

const App = () => (
  <QueryClientProvider client={queryClient}>
    <Router>
      <Routes>
        <Route path="/posts" element={<PostsLayout />}>
          <Route index element={<AllPosts />} />
          <Route path=":id" element={<OnePost />} />
        </Route>
        <Route path="*" element={<Navigate to="/posts" replace />} />
      </Routes>
    </Router>
  </QueryClientProvider>
);

Update AllPposts to use the useOutletContext hook to access the provided posts context value.

import BlogCard from "../../components/BlogCard";
import { useOutletContext } from "react-router-dom";

const AllPosts = () => {
  const { posts } = useOutletContext();

  return (
    <div>
      <h1>Top Web Development Resources 2023</h1>
      <ul>
        {posts.map((post) => (
          <BlogCard {...post} key={post.id} />
        ))}
      </ul>
    </div>
  );
};

Update BlogCard to correctly render that link to the details route.

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

const BlogCard = ({ coverPhoto, id, title }) => {
  return (
    <div className="card">
      <Link to={`/posts/${id}`}>
        <h2>{title}</h2>
        <img
          src={coverPhoto ? coverPhoto.url : null}
          alt=""
          height={200}
          width={200}
        />
      </Link>
    </div>
  );
};

Update OnePost to access the id route path parameter and the posts array and find the matching post by id.

import { useOutletContext, useParams } from "react-router-dom";

const OnePost = () => {
  const { id } = useParams();
  const { posts } = useOutletContext();

  const post = posts.find((post) => post.id === id);

  if (!post) {
    return <div>No post.</div>;
  }

  return (
    <div>
      <h1>One Post Page</h1>
      <img
        src={post.coverPhoto ? post.coverPhoto.url : null}
        alt="cover art"
        height={200}
        width={600}
      />
      <h2>{post.title}</h2>
    </div>
  );
};

Edit react-js-render-new-page-from-api-data-using-slug-click-on-an-article-from-a-l

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.