我基本上是一个反应新手。我目前正在使用 ReactJS 构建一个应用程序,所以这是我的问题。我有这个
Modal
和 Home
组件。每当我单击从 Card
中的组件之一从我的 JSON 文件映射的 Home
组件时。它将显示一个模式,其中显示 JSON 文件中的一组数据。然后模态中还有另一个 Link to
,它将随机生成并从我的 JSON 中提取数据,并重新使用模态组件来显示一组新数据。问题是,我使用了 React Routing(动态路由),但它没有根据我的数据中的唯一键(id)更新 URL。我尝试使用generatePath 和导航,但我想我有点迷失了。这是我的代码的一些预览:
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>
)
}
应用程序.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>
);
}
这里还有该应用程序的工作沙箱演示。
您可以根据 url 对动态内容使用动态路由。
在 home.js 中
import React from 'react';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import Home from './Home';
import AirportDetails from './AirportDetails';
const App = () => {
return (
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/airport/:id" element={<AirportDetails />} />
// dynamic route according to id.
</Routes>
</Router>
);
};
export default App;
并使用 useParams 挂钩提取 AirportDetails.jsx 中的值
import React from 'react';
import { useParams } from 'react-router-dom';
import data from '../airports.json';
const AirportDetails = () => {
const { id } = useParams();
const airport = data.find((airport) => airport.id === id);
if (!airport) {
return <h2>Airport not found</h2>;
}
return (
<div>
<h1>{airport.name}</h1>
<p>ID: {airport.id}</p>
<p>City: {airport.city}</p>
</div>
);
};
export default AirportDetails;
由于您尚未设置动态路线,因此无法使用导航到“./airport/:id”来显示下一个机场的内容。相反,您只是通过 setData 更改数据状态,如果您导航到下一个 url,该数据状态将被清除。如果您仍然想使用该方法,则必须将这些数据存储到全局上下文中。