【问题标题】:React router dom dynamic routingReact路由器dom动态路由
【发布时间】:2022-01-21 16:30:21
【问题描述】:

我正在做一个 React.js 项目。我正在从Star Wars API 中检索 dat,在屏幕上呈现电影列表,现在我正试图通过 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中的全部代码。

【问题讨论】:

  • films 最初是一个数组,但您正在访问它,就像它是一个对象一样,即films.properties.title。它是哪一个?设置组件 key 时,您像对象一样访问,但将整个 films 状态作为道具传递。
  • 谢谢@DrewReese 我解决了这个问题。但即便如此,它也会呈现一个空白页面。即使我将 ItemContainer 的返回更改为

标签: javascript reactjs routes react-router react-router-dom


【解决方案1】:

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 中访问的参数。子路径"film" 应该与您在Home 中链接的内容相匹配。在Home 中确保链接到/"film/...." 路径。

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

    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 道具对象属性上使用可选链接,或者仅当films 状态有要显示的内容时有条件地呈现MovieDetail

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

演示:

【讨论】:

  • 非常感谢 Drew 的解释和代码。但是,路由不起作用。它进入一个空白页面。但是,如果在 App.js 中我将路线从 /films 更改为 /film,它也会进入一个空白页面,但我可以看到导航栏,没有别的。无论我使用哪种方式,都会有一条警告消息:index.tsx:25 No routes match location "/films/1"。此外,该组件的 console.log 不会显示在开发工具上。帖子中有一个更新的沙箱,其中添加了您的新代码。
  • @JoaquinPalacios 你把一些事情搞混了。正在按标题搜索电影,并与剧集 ID、路径等进行比较...请检查更新以使用分叉的 CSB 来回答。
  • 感谢您的帮助德鲁。感谢您的帖子,我学到了很多东西。
【解决方案2】:

像这样修改 App.js:

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

【讨论】:

  • 谢谢礼萨。它可以工作,但它不会路由到 ItemContainer。
猜你喜欢
  • 1970-01-01
  • 2017-02-02
  • 1970-01-01
  • 2018-01-28
  • 2018-05-18
  • 1970-01-01
  • 1970-01-01
  • 2022-01-09
  • 1970-01-01
相关资源
最近更新 更多