将 react-router-dom v6 传递路径作为渲染元素的关键

Make react-router-dom v6 pass path as key to rendered element

提问人:kael 提问时间:3/9/2022 更新时间:3/9/2022 访问量:1519

问:

我想我可能需要在这里改变我的思维范式,所以我在这里也对这类答案持开放态度。

请看以下简化示例:

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 文档,但没有找到任何表明有办法自动执行此操作的内容。我一定是想错了......任何建议都是值得赞赏的。

reactjs 反应路由器 dom

评论

0赞 Matthew Liu 12/31/2022
对不起,这已经晚了 - 我最终做了一些与您的组件非常相似的事情,因为这样路由中的每个帖子都有不同的键。PostRenderer 也是可重用的,所以我并没有真正认为这是一个问题。接受的答案不适用于设置初始状态,因为根据这篇文章 beta.reactjs.org/learn/......我们从未重置过状态PostRendereruseState

答:

1赞 Drew Reese 3/9/2022 #1

我认为一个更常见和实用的解决方案是将 作为依赖项添加到 当路由参数更改时重新运行异步逻辑。postIduseEffect

例:

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很高兴听到,也很高兴提供帮助。干杯,祝你好运!