在单击组件的每个按钮上动态更新 URL

问题描述 投票:0回答:1

我基本上是一个反应新手。我目前正在使用 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>
  );
}

这里还有该应用程序的工作沙箱演示

reactjs react-hooks react-router react-router-dom
1个回答
0
投票

您可以根据 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,该数据状态将被清除。如果您仍然想使用该方法,则必须将这些数据存储到全局上下文中。

© www.soinside.com 2019 - 2024. All rights reserved.