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;
更新
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>
在 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]);
您还应该在 MovieDetail
中嵌套更深的 films
prop object 属性上使用可选链接,或者仅在以下情况下有条件地呈现 MovieDetail
films
状态有东西要显示。
const MovieDetail = ({ films }) => {
return (
<div>
<h1>{films.properties?.title}</h1>
<h3>{films.description}</h3>
</div>
);
};
演示:
我正在做一个 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;
更新
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>
在
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]);
您还应该在
MovieDetail
中嵌套更深的films
prop object 属性上使用可选链接,或者仅在以下情况下有条件地呈现MovieDetail
films
状态有东西要显示。const MovieDetail = ({ films }) => { return ( <div> <h1>{films.properties?.title}</h1> <h3>{films.description}</h3> </div> ); };
演示: