How to use params.(key) to get specific data for each route?

Viewed 72

I want to build a car list app that will show a list of cars, and when I click on a car's details button, it will route it to another component/page that will show the details of cars. I can get the key (vin for me) but I want to get the details of the car for each key(vin) on this page.It's like http://localhost:3000/cars/WAUZZZ4G6FN052847= key. So when a car's key will show up, all details will come due to their key number. Thank you.

index.html

import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import { BrowserRouter, Routes, Route, Outlet, } from "react-router-dom";
import DummyComponent from "./components/DummyComponent";
import CarDetails from "./pages/CarDetails";
import { Home } from "@mui/icons-material";
import Cars from "./pages/Cars";



const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(


  <BrowserRouter>
    <Routes>
      <Route path="/" element={<App />} />
      <Route path="/cars" element={<Outlet />} >
        <Route path="list" element={<Cars />} />
        <Route path=":vin" element={<CarDetails />} />
      </Route>
      <Route path="Home" element={<Home />} />
      <Route path="DummyComponent" element={<DummyComponent />} />

    </Routes>
  </BrowserRouter>

);

Cars.js

import axios from "axios";
import React, { useEffect, useState } from "react";
import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import ListItemButton from '@mui/material/ListItemButton';
import ListItemText from '@mui/material/ListItemText';
import { Link } from "react-router-dom";



const App = () => {


    const [cars, setCars] = useState([])
    const getCarData = async () => {
        try {
            const data = await axios.get("https://react-challenge-api.azurewebsites.net/vehicles")
            setCars(data.data)
        }
        catch (e) {
            console.log(e)
        }
    }

    useEffect(() => {
        getCarData()
    }, [])
    return (

        <div className="App">

            <List sx={{ width: '100%', maxWidth: 600, bgcolor: 'background.paper' }}>
                {cars.map((car) => (

                    <ListItemButton key={car.vin}>
                        <ListItem
                            key={car.vin}
                            disableGutters

                            secondaryAction={
                                <ListItemButton >
                                    <Link to={`/cars/${car.vin}`}>details</Link>

                                </ListItemButton>
                            }
                        >
                            <ListItemText key={car.vin} primary={car.model_variant} />

                        </ListItem>
                    </ListItemButton>

                ))
                }
            </List >
        </div >
    );
};

export default App;

CarDetails.js (I want to show each data in this component, I used params but I don't know how to get data due to params.

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

const CarDetails = () => {

    let params = useParams();

    return (
        <>
            <h1>car</h1>
            <ul>
                this is your {params.vin}
            </ul>
        </>
    )
}



export default CarDetails;
1 Answers

I would suggest moving the car data fetching into a layout route and pass the cars state down in an Outlet context.

Example:

Cars - Handles fetching the car data and passes the cars state along to nested routes via the Outlet component's context.

const Cars = () => {
  const [cars, setCars] = useState([]);

  const getCarData = async () => {
    try {
      const data = await axios.get(
        "https://react-challenge-api.azurewebsites.net/vehicles"
      );
      setCars(data.data);
    } catch (e) {
      console.log(e);
    }
  };

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

  return <Outlet context={{ cars }} />;
};

CarList - Reads the cars state from the Outlet context and renders the list.

const CarList = () => {
  const { cars } = useOutletContext();

  return (
    <List sx={{ width: "100%", maxWidth: 600, bgcolor: "background.paper" }}>
      {cars.map((car) => (
        <ListItemButton key={car.vin}>
          <ListItem
            key={car.vin}
            disableGutters
            secondaryAction={
              <ListItemButton>
                <Link to={`/cars/${car.vin}`}>details</Link>
              </ListItemButton>
            }
          >
            <ListItemText key={car.vin} primary={car.model_variant} />
          </ListItem>
        </ListItemButton>
      ))}
    </List>
  );
};

CarDetails - Takes the vin route path param and the cars array from the outlet context and searches for a matching car object.

const CarDetails = () => {
  const { vin } = useParams();
  const { cars } = useOutletContext();

  const car = cars.find((car) => car.vin === vin);

  if (!car) {
    return "No car matches this VIN";
  }

  return (
    <>
      <h1>{car.model_variant}</h1>
      <ul>
        <li>Body: {car.body_type}</li>
        <li>Doors: {car.doors}</li>
        <li>Fuel: {car.fuel_type}</li>
        <li>VIN: {car.vin}</li>
        <li>Registration #: {car.regno}</li>
        <li>Transmission: {car.transmission_type}</li>
      </ul>
    </>
  );
};

Routes

<Routes>
  <Route path="/" element={<App />} />
  <Route path="/cars" element={<Cars />}> // <-- layout route renders outlet
    <Route path="list" element={<CarList />} />
    <Route path=":vin" element={<CarDetails />} />
  </Route>
  <Route path="Home" element={<Home />} />
  <Route path="DummyComponent" element={<DummyComponent />} />
</Routes>

Edit how-to-use-params-key-to-get-specific-data-for-each-route

Related