提问人:kael 提问时间:3/9/2022 更新时间:3/9/2022 访问量:1519
将 react-router-dom v6 传递路径作为渲染元素的关键
Make react-router-dom v6 pass path as key to rendered element
问:
我想我可能需要在这里改变我的思维范式,所以我在这里也对这类答案持开放态度。
请看以下简化示例:
export const App = (p: { apiClient: SomeApiClient }) => {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/posts/:postId" element={<Post apiClient={p.apiClient} />} />
</Routes>
);
}
export const Home = () => {
return <h1>Home!</h1>
}
export const Post = (p: { apiClient: SomeApiClient }) => {
const { postId } = useParams();
const [ state, setState ] = useState<PostState>({ status: "loading" });
// When the component mounts, get the specified post from the API
useEffect(() => {
if (state.status === "loading") {
(async () => {
const post = await p.apiClient.getPost(postId);
setState({ status: "ready", post });
})();
}
})
return (
<h2>Posts</h2>
{
state.status === "loading"
? <p>Loading....</p>
: <div className="post">
<h3>{state.post.title}</h3>
<div className="content">{state.post.content}</div>
</div>
}
)
}
export type PostState =
| { status: "loading" }
| { status: "ready"; post: BlogPost };
export type BlogPost = { title: string; content: string };
这在第一次工作正常,但假装页面上有一个进入下一篇文章。当我单击该链接时,URL 会更改,但页面内容不会更改,因为 React Router 实际上并没有重新挂载组件。该组件正确接收更新的 postId 并重新呈现,但由于它不会重新装载,因此逻辑不会再次运行,内容保持不变。<Link />
<Post .../>
useEffect
我一直在通过创建这样的中间组件来非常笨拙地解决这个问题:
export const App = (p: { apiClient: SomeApiClient }) => {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/posts/:postId" element={<PostRenderer apiClient={p.apiClient} />} />
</Routes>
);
}
export const PostRenderer = (p: { apiClient: SomeApiClient }) => {
const { postId } = useParams();
return <Post key={postId} postId={postId} apiClient={p.apiClient} />
}
export const Post = (p: { postId: string; apiClient: SomeApiClient }) => {
// ....
}
但是我开始得到很多这样的参数,从字面上看,他们所做的只是从 URL 中获取参数并将其用作实际目标组件上的键。我已经通读了 react-router-dom 文档,但没有找到任何表明有办法自动执行此操作的内容。我一定是想错了......任何建议都是值得赞赏的。
答:
1赞
Drew Reese
3/9/2022
#1
我认为一个更常见和实用的解决方案是将 作为依赖项添加到 当路由参数更改时重新运行异步逻辑。postId
useEffect
例:
export const Post = (p: { apiClient: SomeApiClient }) => {
const { postId } = useParams();
const [state, setState] = useState<PostState>({ status: "loading" });
// When the post id updates, get the specified post from the API
useEffect(() => {
const fetchPostById = async (postId) => {
setState({ status: "loading" });
const post = await p.apiClient.getPost(postId);
setState({ status: "ready", post });
};
fetchPostById(postId);
}, [postId]);
return (
<h2>Posts</h2>
{
state.status === "loading"
? <p>Loading....</p>
: <div className="post">
<h3>{state.post.title}</h3>
<div className="content">{state.post.content}</div>
</div>
}
)
};
评论
0赞
kael
3/9/2022
啊哈!感谢您的推动。我将围绕这种模式进行重构,看看它是否有助于清理问题。我是反应的新手,所以如果这个简单的转变改善了我的问题,我不会感到惊讶。如果它最终让我回家,我会回来接受这个答案。
0赞
Drew Reese
3/9/2022
@kael 如果它甚至让你离家更近,那也是一种改进,对吧?如果它有帮助,请告诉我,如果我们需要改进它,我们可以。
1赞
kael
3/11/2022
赤穗经过重大重构后,这被证明是一个非常有用的建议。谢谢!
0赞
Drew Reese
3/11/2022
@kael很高兴听到,也很高兴提供帮助。干杯,祝你好运!
评论
PostRenderer
useState