React router dom 动态路由

React router dom dynamic routing

我正在做一个 React.js 项目。我正在从 Star Wars API 在屏幕上渲染电影列表中检索数据,现在我正试图通过 react-router-dom 将每部电影路由到它自己的页面。不幸的是,我无法让它发挥作用。当我尝试动态路由时它崩溃了。

REZA 回答后的更新

这是App.js:

import './App.css';    
import { Route, Routes } from "react-router-dom";    
import Home from './components/Home';
import ItemContainer from './components/ItemContainer';
import Navbar from './components/Navbar';

function App() {
  return (
      <>
      <Navbar />
        <Routes>
            <Route exact path='/' element={<Home />} />
            <Route exact path="/:movieId" element={<ItemContainer />} />
        </Routes> 
        </> 
  );
}    
export default App;

这是 ItemContainer:

import { useEffect, useState } from "react";    
import MovieDetail from "../MovieDetail";
import { useParams } from "react-router-dom";    
const ShowMovie = (movieId) => {
    const [result, setResult] = useState([]);

    const fetchData = async () => {
        const res = await fetch("https://www.swapi.tech/api/films/");
        const json = await res.json();
        setResult(json.result);
    }
    useEffect(() => {
        fetchData();
    }, []);
    return new Promise((res) => 
    res(result.find((value) => value.properties.title === movieId)))
}

const ItemContainer = () => {
    const [films, setFilms] = useState([]);
    const { movieId } = useParams([]);
    console.log('params movieId container', movieId)

    useEffect(() => {
        ShowMovie(movieId).then((value) => {
            setFilms(value.properties.title)
        })
    }, [movieId])
    return (
            <MovieDetail key={films.properties.title} films={films} />
    );
}     
export default ItemContainer;

console.log什么都没给。

更新

此外,这是 sandbox 中的全部代码。

像这样修改App.js:

function App() {
  return (
    <>
      <Navbar />
      <Routes>
        <Route exact path="/" element={<Home />} />
        <Route exact path="/MovieDetail/:movieId" element={<ItemContainer />} />
      </Routes>
    </>
  );
}

ShowMovie 像 React 组件一样声明,但像实用函数一样使用。你不应该直接调用 React 函数组件。 React 函数也应该是同步的、纯函数。 ShowMovie 正在返回一个 Promise 使其成为一个异步函数。

ShowMovie转换成实用函数,它基本上会调用fetch并处理JSON响应。

import { useEffect, useState } from "react";    
import MovieDetail from "../MovieDetail";
import { useParams } from "react-router-dom";

const showMovie = async (movieId) => {
  const res = await fetch("https://www.swapi.tech/api/films/");
  const json = await res.json();
  const movie = json.result.find((value) => value.properties.episode_id === Number(movieId)));

  if (!movie) {
    throw new Error("No match found.");
  }

  return movie;
}

const ItemContainer = () => {
  const [films, setFilms] = useState({});
  const { movieId } = useParams();

  useEffect(() => {
    console.log('params movieId container', movieId);

    showMovie(movieId)
      .then((movie) => {
        setFilms(movie);
      })
      .catch(error => {
        // handle error/log it/show message/ignore/etc...

        setFilms({}); // maintain state invariant of object
      });
  }, [movieId]);

  return (
    <MovieDetail key={films.properties?.title} films={films} />
  );
};

export default ItemContainer;

更新

  1. route 路径应该包括 movieId,您在 ItemContainer 中访问的参数。 sub-path "film" 应该与 Home 中的 link 匹配。在 Home 中确保你 link 到 /"film/...." 路径。

    <Routes>
      <Route path="/films" element={<Home />} />
      <Route path="/film/:movieId" element={<ItemContainer />} />
      <Route path="/" element={<Navigate replace to="/films" />} />
    </Routes>
    
  2. ItemContainer 中,您应该将电影 object 的 episode_id 属性 与 movieId 值相匹配。将 整个 电影 object 存储到状态中,而不仅仅是标题。

    const showMovie = async (movieId) => {
      const res = await fetch("https://www.swapi.tech/api/films/");
      const json = await res.json();
      const movie = json.result.find((value) => value.properties.episode_id === Number(movieId)));
    
      if (!movie) {
        throw new Error("No match found.");
      }
    
      return movie;
    }
    

    ...

    useEffect(() => {
      console.log("params movieId container", movieId);
    
      showMovie(movieId)
        .then((movie) => {
          setFilms(movie);
        })
        .catch((error) => {
          // handle error/log it/show message/ignore/etc...
    
          setFilms({}); // maintain state invariant of object
        });
    }, [movieId]);
    
  3. 您还应该在 MovieDetail 中嵌套更深的 films prop object 属性上使用可选链接,或者仅在以下情况下有条件地呈现 MovieDetail films 状态有东西要显示。

    const MovieDetail = ({ films }) => {
      return (
        <div>
          <h1>{films.properties?.title}</h1>
          <h3>{films.description}</h3>
        </div>
      );
    };
    

演示: