I’m basically a newbie to react. And I’m currently building an app using ReactJS, so here’s my problem. I have this Modal
and Home
components. Whenever I click a Card
component that is mapped from my JSON file from one of the components in Home
. It will show a modal where it will display a set of data from a JSON file. Then there is another Link to
inside the modal where it will randomly generate and pull data from my JSON and re use the Modal component to display a new set of data. The thing is, I have used React Routing(dynamically routing) but it’s not updating the URL based on the unique key(id) from my data. I tried using generatePath and navigate, but I guess i’m a little bit lost. Here’s a little preview of my code:
Home.js
import { useEffect, useState, useMemo } from "react";
import React from "react";
import data from "../airports.json";
import WebFont from "webfontloader";
import Layout from "./Layout";
import { Link, useLocation, useNavigate, generatePath } from "react-router-dom";
export function Home() {
const [airports, setAirports] = useState(data);
const [filteredAirports, setFilteredAirports] = useState(data);
const onSearchTerm = (term) => {
const filteredData = airports.filter(
(airport) =>
airport.id.toLowerCase().includes(term.toLowerCase()) ||
airport.name.toLowerCase().includes(term.toLowerCase()) ||
airport.city.toLowerCase().includes(term.toLowerCase()),
);
setFilteredAirports(filteredData);
};
useEffect(() => {
WebFont.load({
google: {
families: ["Poppins", "Catamaran"],
},
});
}, []);
return (
<>
<Layout onSearchTerm={onSearchTerm} />
<ul className="cf airport-list">
<Airports airports={filteredAirports} />
</ul>
</>
);
}
export function Airports({ airports }) {
const sorted = airports.sort((a, b) => a.id.localeCompare(b.id)); // alphabetically sort by id before mapping the data
const airportCards = sorted.map((airport, index) => {
return (
<li
key={index}
className={`card ${airport.id} loaded`}
react-id={airport.index}
>
<AirportCard
airport={airport}
uniqueId={airport.id}
/>
</li>
);
});
return <ul>{airportCards}</ul>;
}
function capitalize(str) {
return str.toUpperCase();
}
function AirportCard({ airport, uniqueId }) {
const location = useLocation();
function setImageUrl() {
return {
backgroundImage: `url(${process.env.PUBLIC_URL}/assets/images/cards/${airport.id}.jpg)`,
};
}
const id = airport.id;
return (
<>
<div className="background" style={setImageUrl()}></div>
<Link
to={{ pathname: `/airport/${id}`}}
className="modal-overlay"
state={{ previousLocation: location }}
>
{airport.id}
</Link>
</>
);
}
export function AirportDetails({ id, airportData }) {
// Initialize data to specific airport by id,
// otherwise select a random airport.
const [data, setData] = useState(() => {
const airport = airportData.find(
(airport) => airport.id.toLowerCase().includes(id.toLowerCase())
);
return airport ? airport : randomAirport(airportData);
});
const airport = airportData.filter((airport) =>
airport.id.toLowerCase().includes(id.toLowerCase()),
)[0];
const navigate = useNavigate();
const location = useLocation();
console.log(location);
const [description, setDescription] = useState(airport.description);
const pattern = /*([A-Za-z])*/gi;
const em = description.replace(pattern, "<em>$1</em>");
const currUrl = window.location.href;
function capitalize(str) {
return str.toUpperCase();
}
const share = (socialType) => (e) => {
if (socialType == "twitter") {
const text = `Making sense of those three-letter airport codes: ${capitalize(
id,
)}`;
const link = `https://twitter.com/intent/tweet?url=${currUrl}&text=${text}`;
return link;
}
const link = `https://www.facebook.com/dialog/share?display=popup&href=${currUrl}${id}&redirect_uri=${currUrl}${id}`;
return link;
};
function setTo(social) {
if (social == "twitter") {
return "https://twitter.com/intent/tweet?url=$SHARE_URL&text=$TEXT";
} else {
return "https://www.facebook.com/sharer/sharer.php?u=$SHARE_URL";
}
}
if (!airport) {
return navigate("/"); // or navigate back, etc.
}
return (
<div className={`detail ${id} overlay`} style={setImageUrlLarge(data.id)}>
<a className='overlay' rel='noopener'></a>
<div className='container'>
<div className='detail-info'>
<h1>{data.id}</h1>
<h2>{data.name}</h2>
<h3><span className="local_name">{data.local_name}</span></h3>
<h4>{data.city}</h4>
<div className="description fl-edu">
<p dangerouslySetInnerHTML={{ __html: em }}></p>
</div>
<a
className="close-detail"
role="button"
onClick={() => navigate("/")}
></a>
<a
className="random"
role="button"
onClick={() => {
// Enqueue state update to new random airport data
setData(randomAirport(airportData));
// navigate(generatePath("/airport/:id", { id: `${data.id}`}));
}}
>
Random Airport
</a>
<div className="social">
<a
role="button"
className="twitter"
href={setTo("twitter")}
onClick={() => {
share("twitter");
}}
target="_blank"
></a>
</div>
<div className="social">
<a
className="facebook"
href={setTo("facebook")}
onClick={() => {
share("facebook");
}}
target="_blank"
></a>
</div>
</div>
<div className="photo-credit">
Photo by <a>{data.imageCredit}</a>
</div>
<a className="back" role="button" onClick={() => navigate("/")}>
Airport Codes PH
</a>
</div>
</div>
);
}
function setImageUrlLarge(url) {
return {
backgroundImage: `url(${process.env.PUBLIC_URL}/assets/images/large/${url}.jpg)`,
};
}
function randomAirport(airportData) {
const rand = Math.floor(Math.random() * airportData.length);
// console.log(airportData[rand]);
return airportData[rand];
}
Modal.js
import { useEffect, useRef, Fragment, Link, useState } from "react";
import { useParams } from "react-router-dom";
import { disableBodyScroll, enableBodyScroll } from "body-scroll-lock";
import airportData from '../airports.json';
import { AirportDetails } from "./Home";
export function Modal() {
const modalRef = useRef();
const { id } = useParams();
useEffect(() => {
const observerRefValue = modalRef.current;
disableBodyScroll(observerRefValue);
return () => {
if (observerRefValue) {
enableBodyScroll(observerRefValue);
}
};
}, []);
function setImageUrlLarge(url) {
return {
backgroundImage: `url(${process.env.PUBLIC_URL}/assets/images/large/${url}.jpg)`
}
}
return (
<div ref={modalRef} className="modal-wrapper">
<div className="modal">
<AirportDetails id={id} airportData={airportData} />
</div>
</div>
)
}
App.js
import './App.css';
import { Routes, Route, useLocation } from "react-router-dom";
import React from 'react';
import Layout from './components/Layout';
import { Home } from "./components/Home";
import { About } from "./components/About";
import { Modal } from "./components/Modal";
import { NoMatch } from "./components/NoMatch";
export default function App() {
const location = useLocation();
const previousLocation = location.state?.previousLocation;
return (
<div className="app">
<Routes location={previousLocation || location}>
<Route path="/" element={<Layout />}></Route>
<Route index element={<Home />}/>
<Route path="/about" element={<About />} />
<Route path="*" element={<NoMatch />} />
</Routes>
{previousLocation && (
<Routes>
<Route path="/airport/:id" element={<Modal />}/>
</Routes>
)}
</div>
);
}
Also here’s a working sandbox demo for the app.
krxbnx is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.