In the past I wrote an article about how to get data dynamically when we access the parameters of the route using the react router dom.
However, using parameters is not the only solution we have, so today I decided to publish this article to show an example of how to use query strings using the react router dom.
Although this approach is quite common on the backend, on the frontend it is not used that much, however many world-known platforms use them (such as Youtube).
My reason for using this approach is very simple, if I have to use several parameters or I don't know how many parameters I'm going to use and I don't want to be constantly struggling with routes, I use query strings.
Let's code
In today's example we are going to create an application that contains two pages. On the main page we will list a large number of albums and when we click on one of them we will be redirected to a page to show some details about the specific album, such as the album name and the author's name.
The data that we will have in our application comes from the jsonplaceholder API and http requests will be made using axios.
The dependencies that we will use will be as follows:
npm install react-router-dom axios
Now we can start defining our routes in App.jsx
:
// @src/App.jsx
import React from "react";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import Home from "./Pages/Home";
import Album from "./Pages/Album";
const App = () => {
return (
<Router>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/album" component={Album} />
</Switch>
</Router>
);
};
export default App;
As you may have noticed, in the /album
route no parameters were defined, this is because the query strings are used as follows (in case you are not used to it):
/album?id=56&artistId=7
Now we can create the components of our pages, first let's work in Home.jsx
:
// @src/Pages/Home.jsx
import React from "react";
const Home = () => {
return <h1>Main page</h1>
};
export default Home;
First let's work our state using the useState()
hook, as we know we're going to have a list of albums, the initial state will be an array.
// @src/Pages/Home.jsx
import React, { useState } from "react";
const Home = () => {
const [albums, setAlbums] = useState([]);
return <h1>Main page</h1>
};
export default Home;
And I want the http request to be done as soon as the component is rendered, for that we will use the useEffect()
hook. We will also use axios to make the http request to the API and we will store the response data in our state.
// @src/Pages/Home.jsx
import React, { useState, useEffect } from "react";
import axios from "axios";
const Home = () => {
const [albums, setAlbums] = useState([]);
useEffect(() => {
const fetch = async () => {
try {
const { data } = await axios.get("https://jsonplaceholder.typicode.com/albums");
setAlbums(data);
} catch (err) {
console.error(err);
}
};
fetch();
}, []);
return <h1>Main page</h1>
};
export default Home;
Now we just need to work on our component template.
// @src/Pages/Home.jsx
import React, { useState, useEffect } from "react";
import axios from "axios";
const Home = () => {
const [albums, setAlbums] = useState([]);
useEffect(() => {
const fetch = async () => {
try {
const { data } = await axios.get("https://jsonplaceholder.typicode.com/albums");
setAlbums(data);
} catch (err) {
console.error(err);
}
};
fetch();
}, []);
return (
<article>
<code>Albums</code>
{albums.map((el) => (
<div key={el.id}>
<h2>{el.title}</h2>
</div>
))}
</article>
);
};
export default Home;
Now let's import the Link
component from the react router dom to redirect the user to the details page. However, we will have to add first to creating our query string before we create it. Like this:
// @src/Pages/Home.jsx
import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import axios from "axios";
const Home = () => {
// Hidden for simplicity
return (
<article>
<code>Albums</code>
{albums.map((el) => (
<div key={el.id}>
<Link to={`/album?id=${el.id}&artistId=${el.userId}`}>
<h2>{el.title}</h2>
</Link>
</div>
))}
</article>
);
};
export default Home;
As you may have noticed, let's pass the album id
and the album artistId
(which corresponds to the userId).
The current result should look like this:
Now we can start working on the detail component, which we'll name Album.jsx
:
// @src/Pages/Album.jsx
import React from "react";
const Album = () => {
return <h1>Details page</h1>
};
export default Album;
Let's start again by working the state of our component. In this case we know that we are going to have two states, one of them will be album data and the other will contain artist data. And we know that both will be objects.
// @src/Pages/Album.jsx
import React, { useState } from "react";
const Album = () => {
const [album, setAlbum] = useState({});
const [artist, setArtist] = useState({});
return <h1>Details page</h1>
};
export default Album;
Now we can get our query string values, but first we need to import the react router dom's useLocation()
hook. This hook returns an object that matches the current URL.
Then we will search all existing parameters in the URL and we will get each one of our query strings individually, as follows:
// @src/Pages/Album.jsx
import React, { useState } from "react";
import { useLocation } from "react-router-dom";
const Album = () => {
const [album, setAlbum] = useState({});
const [artist, setArtist] = useState({});
const query = new URLSearchParams(useLocation().search);
const id = query.get("id");
const artistId = query.get("artistId");
return <h1>Details page</h1>
};
export default Album;
Now let's use the useEffect()
hook again to execute the http requests when the component is rendered.
// @src/Pages/Album.jsx
import React, { useEffect, useState } from "react";
import { useLocation } from "react-router-dom";
const Album = () => {
const [album, setAlbum] = useState({});
const [artist, setArtist] = useState({});
const query = new URLSearchParams(useLocation().search);
const id = query.get("id");
const artistId = query.get("artistId");
useEffect(() => {
// Logic goes here
}, []);
return <h1>Details page</h1>
};
export default Album;
Now let's create the function that will be called to execute the http requests.
// @src/Pages/Album.jsx
import React, { useEffect, useState } from "react";
import { useLocation } from "react-router-dom";
const Album = () => {
const [album, setAlbum] = useState({});
const [artist, setArtist] = useState({});
const query = new URLSearchParams(useLocation().search);
const id = query.get("id");
const artistId = query.get("artistId");
useEffect(() => {
const fetch = async () => {
try {
// More logic goes here
} catch (err) {
console.error(err);
}
};
fetch();
}, []);
return <h1>Details page</h1>
};
export default Album;
Unlike other examples I've done in the past, this time we'll do http requests simultaneously. To do this we will first define the two http requests using the axios, one of them will be to fetch the album data and the other for the user.
// @src/Pages/Album.jsx
import React, { useEffect, useState } from "react";
import { useLocation } from "react-router-dom";
import axios from "axios";
const Album = () => {
const [album, setAlbum] = useState({});
const [artist, setArtist] = useState({});
const query = new URLSearchParams(useLocation().search);
const id = query.get("id");
const artistId = query.get("artistId");
useEffect(() => {
const fetch = async () => {
try {
const getAlbum = axios.get(`https://jsonplaceholder.typicode.com/albums/${id}`);
const getArtist = axios.get(`https://jsonplaceholder.typicode.com/users/${artistId}`);
// Even more logic goes here
} catch (err) {
console.error(err);
}
};
fetch();
}, []);
return <h1>Details page</h1>
};
export default Album;
Now we will use the axios .all()
method which will be used to execute both http requests at the same time. And let's pass the two http requests that we defined. Like this:
// @src/Pages/Album.jsx
import React, { useEffect, useState } from "react";
import { useLocation } from "react-router-dom";
import axios from "axios";
const Album = () => {
const [album, setAlbum] = useState({});
const [artist, setArtist] = useState({});
const query = new URLSearchParams(useLocation().search);
const id = query.get("id");
const artistId = query.get("artistId");
useEffect(() => {
const fetch = async () => {
try {
const getAlbum = axios.get(`https://jsonplaceholder.typicode.com/albums/${id}`);
const getArtist = axios.get(`https://jsonplaceholder.typicode.com/users/${artistId}`);
const responses = await axios.all([getAlbum, getArtist]);
// Almost done
} catch (err) {
console.error(err);
}
};
fetch();
}, []);
return <h1>Details page</h1>
};
export default Album;
What axios.all
will return to us is an array of promises and we just need to get the values of the responses from those same promises and store them in the respective states.
// @src/Pages/Album.jsx
import React, { useEffect, useState } from "react";
import { useLocation } from "react-router-dom";
import axios from "axios";
const Album = () => {
const [album, setAlbum] = useState({});
const [artist, setArtist] = useState({});
const query = new URLSearchParams(useLocation().search);
const id = query.get("id");
const artistId = query.get("artistId");
useEffect(() => {
const fetch = async () => {
try {
const getAlbum = axios.get(`https://jsonplaceholder.typicode.com/albums/${id}`);
const getArtist = axios.get(`https://jsonplaceholder.typicode.com/users/${artistId}`);
const responses = await axios.all([getAlbum, getArtist]);
setAlbum(responses[0].data);
setArtist(responses[1].data);
} catch (err) {
console.error(err);
}
};
fetch();
}, []);
return <h1>Details page</h1>
};
export default Album;
Now we can start working on our template:
// @src/Pages/Album.jsx
import React, { useEffect, useState } from "react";
import { useLocation } from "react-router-dom";
import axios from "axios";
const Album = () => {
// Hidden for simplicity
return (
<article>
<code>Song details</code>
<div>
<h1>{album.title}</h1>
<p>by: {artist.name}</p>
<button>Go back</button>
</div>
</article>
);
};
export default Album;
Finally we just need to import the useHistory()
hook from the react router dom so that we can go back to the main page once the button is clicked.
// @src/Pages/Album.jsx
import React, { useEffect, useState } from "react";
import { useHistory, useLocation } from "react-router-dom";
import axios from "axios";
const Album = () => {
const { push } = useHistory();
// Hidden for simplicity
return (
<article>
<code>Song details</code>
<div>
<h1>{album.title}</h1>
<p>by: {artist.name}</p>
<button onClick={() => push("/")}>Go back</button>
</div>
</article>
);
};
export default Album;
The current result should look like this:
Conclusion
As always, I hope it was both clear and simple, so that you can implement it in your projects. Have a great day! 👏 ☺️
Top comments (3)
We could have used useSearchParams instead of useState for the query params right? reactrouter.com/docs/en/v6/hooks/u...
Thanks for sharing
Thanks for sharing