Get Data using Dynamic Routes in React

One of the most common needs we all have is consuming data in the frontend dynamically and one of the solutions is the use of parameters in our routes.

If you already have some experience with the backend, whether it is creating an Api or consuming an Api through the frontend, we are constantly doing something like this:

/api/v1/posts/10

Which actually corresponds to this:

/api/v1/posts/:id

And by using the react router dom's useParams() hook, we have access to the route parameters of our applications.

So the idea of today's example is to have a list of posts on the main page and when we click on one of them we go to a dynamic page that will only show the post that was selected.

Let's code

First we have to install the following dependencies:

npm i react-router-dom axios

Then in our App.jsx we will define all our routes.

// @src/App.jsx

import React from "react";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";

import Home from "./pages/Home";
import Post from "./pages/Post";

const App = () => {
  return (
    <Router>
      <Switch>
        <Route exact path="/" component={Home} />
        <Route path="/post/:id" component={Post} />
      </Switch>
    </Router>
  );
};

export default App;

In the previous code, in the post route, we added a parameter called id that will later be used to fetch the post in question. And now we have to create each of our pages.

// @src/pages/Home.jsx

import React from "react";

const Home = () => {
  return <h1>Home</h1>
};

export default Home;

First let's deal with the state of our component, for that we'll use the useState() hook. And we already have in mind that we are going to have a list of posts so the state will be an array.

// @src/pages/Home.jsx

import React, { useState } from "react";

const Home = () => {
  const [posts, setPosts] = useState([]);
  return <h1>Home</h1>
};

export default Home;

Now we have to fetch the data as soon as the component is rendered, for that we will use useEffect() and we will consume the jsonplaceholder Api using axios.

// @src/pages/Home.jsx

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

const Home = () => {
  const [posts, setPosts] = useState([]);
  useEffect(() => {
    const fetch = async () => {
      try {
        const { data } = await axios.get("https://jsonplaceholder.typicode.com/posts");
        setPosts(data);
      } catch (err) {
        console.error(err);
      }
    };
    fetch();
  }, []);
  return <h1>Home</h1>
};

export default Home;

Now we can list our posts in the template but first we have to import the Link component from the react router dom. Like this:

// @src/pages/Home.jsx

import React, { useEffect, useState } from "react";
import { Link } from "react-router-dom";
import axios from "axios";

const Home = () => {
  const [posts, setPosts] = useState([]);
  useEffect(() => {
    const fetch = async () => {
      try {
        const { data } = await axios.get("https://jsonplaceholder.typicode.com/posts");
        setPosts(data);
      } catch (err) {
        console.error(err);
      }
    };
    fetch();
  }, []);
  return (
    <>
      {posts.map((el) => (
        <article key={el.id}>
          <Link>
            <h1>{el.title}</h1>
          </Link>
          <p>{el.body}</p>
        </article>
      ))}
    </>
  );
};

export default Home;

Now, in order to dynamically pass the id of the element that is clicked, we have to do it like this:

// @src/pages/Home.jsx

import React, { useEffect, useState } from "react";
import { Link } from "react-router-dom";
import axios from "axios";

const Home = () => {
  // Hidden for simplicity
  return (
    <>
      {posts.map((el) => (
        <article key={el.id}>
          <Link to={`/post/${el.id}`}>
            <h1>{el.title}</h1>
          </Link>
          <p>{el.body}</p>
        </article>
      ))}
    </>
  );
};

export default Home;

And you should get a result similar to this:

Now we can start working on the Post.jsx page.

// @src/pages/Post.jsx

import React from "react";

const Post = () => {
  return <h1>Single Post</h1>
};

export default Post;

Let's start again by working on the component's state and in this case we know that it will have to be an object as it will only be a post.

// @src/pages/Post.jsx

import React, { useState } from "react";

const Post = () => {
  const [post, SetPost] = useState({});
  return <h1>Single Post</h1>
};

export default Post;

Then we'll import the useParams() hook from the react router dom and get the id.

// @src/pages/Post.jsx

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

const Post = () => {
  const { id } = useParams();
  const [post, SetPost] = useState({});
  return <h1>Single Post</h1>
};

export default Post;

Now we can use the useEffect() hook to fetch the post data as soon as the component is rendered. But this time we're going to pass the id that we got dynamically to get that specific post.

// @src/pages/Post.jsx

import React, { useEffect, useState } from "react";
import { useParams } from "react-router-dom";

const Post = () => {
  const { id } = useParams();
  const [post, SetPost] = useState({});
  useEffect(() => {
    const fetch = async () => {
      try {
        const { data } = await axios.get(`https://jsonplaceholder.typicode.com/posts/${id}`);
        SetPost(data);
      } catch (err) {
        console.error(err);
      }
    };
    fetch();
  }, []);
  return <h1>Single Post</h1>
};

export default Post;

Now we can start working on our component template.

// @src/pages/Post.jsx

import React, { useEffect, useState } from "react";
import { useParams } from "react-router-dom";

const Post = () => {
  const { id } = useParams();
  const [post, SetPost] = useState({});
  useEffect(() => {
    const fetch = async () => {
      try {
        const { data } = await axios.get(`https://jsonplaceholder.typicode.com/posts/${id}`);
        SetPost(data);
      } catch (err) {
        console.error(err);
      }
    };
    fetch();
  }, []);
  return (
    <article>
      <h1>{post.title}</h1>
      <p>{post.body}</p>
      <br />
      <button>Go back</button>
    </article>
  );
};

export default Post;

Last but not least, let's use the useHistory() hook to push the user to the main page.

// @src/pages/Post.jsx

import React, { useEffect, useState } from "react";
import { useParams, useHistory } from "react-router-dom";

const Post = () => {
  const { id } = useParams();
  const { push } = useHistory();
  const [post, SetPost] = useState({});
  useEffect(() => {
    const fetch = async () => {
      try {
        const { data } = await axios.get(`https://jsonplaceholder.typicode.com/posts/${id}`);
        SetPost(data);
      } catch (err) {
        console.error(err);
      }
    };
    fetch();
  }, []);
  return (
    <article>
      <h1>{post.title}</h1>
      <p>{post.body}</p>
      <br />
      <button onClick={() => push("/")}>Go back</button>
    </article>
  );
};

export default Post;

The final result of the application should look like the following:

Conclusion

As always, I hope it was clear and that this article has helped you.

Have a great day! 🪗 😁

24