使用 React Router 6 和 Remix 实现 Modals
2010 年代,Pinterest、Instagram 等应用开始流行。模态框通常用作一种“详细信息”视图,用于聚焦集合中的特定对象(例如 Pinterest 图板),同时又不会完全脱离父页面的上下文。
React Router 6 Repository 上有一个示例,介绍如何使用state
并backgroundLocation
保持父页面可见并在顶部显示模式来实现此模式。
如何使用 React Router Data Routers (^6.4.0) 和 Remix 实现同样的效果?让我们来一探究竟!
首先,当使用时,loaders
我们将无法使用相同的设置机制,state.backgroundLocation
因为在我们定义路线时路线状态不存在:
// We can't access state here!
const router = createBrowserRouter([
{
path: '/',
element: <App />,
children: []
}
)]
因此,带有数据加载器的模态框有两种选择,根据您的使用情况,它们都有各自的优缺点:
使用嵌套路由的模态框
第一个选项是使用 React Router 的嵌套路由功能来启用可以在其父路由内显示的模式:
让我们使用 React Router 仓库中的相同示例。我们希望每当用户点击图片时显示一个模态框,并将图片详情视图放在模态框内,而不是跳转到新的页面。
路由配置:
const router = createBrowserRouter([
{
path: '/',
element: <App />,
children: [
{
element: <Layout />,
children: [
{
index: true,
element: <Home />,
},
{
path: 'img/:id',
element: <ImageView />,
},
{
path: 'gallery',
element: <Gallery />,
children: [
{
path: 'img/:id',
element: <Modal />,
},
],
},
{
path: '*',
element: <NoMatch />,
},
],
},
],
},
]);
该Modal
组件现在是组件内的嵌套路由Gallery
。
在 Remix 中,只需创建一个新的文件夹结构即可创建嵌套路由
routes
| index.tsx
| gallery
| index.tsx
| modal.tsx
下一步,我们需要Outlet
向Gallery
组件添加一个来渲染模态框
export function Gallery() {
return (
<div style={{ padding: '0 24px' }}>
<h2>Gallery</h2>
...
<Outlet />
</div>
);
}
优点:
-
模态框可以使用自己的
loaders
:如果您的模态框需要访问数据获取,则可以为它们分配自己的加载器,以将它们与父路由分开并独立工作。 -
它不需要大量的设置:它们只是在父路由之上呈现的嵌套路由。
-
持久导航:由于模式只是常规路线,因此您可以引用它们,使用 URL 路径打开和关闭它们。
缺点:
-
模态框需要在路由下配置,这意味着无法在根
/
URL中呈现它们 -
模态框只会显示在已定义的嵌套路由内,无法从任何页面访问。如果要在其他路径上显示它们,则需要反复手动创建这些路由。
-
如果您想移动模式,则必须重构使用它的整个路线段。
-
有时很难保持背景页面的上下文,因为出口必须位于正确的位置。
使用搜索参数的模态框
嵌套路由的替代方法是使用 URL 中的搜索参数(使用平台!💪)
要打开包含图库中图片的模式,我们需要导航到以下 URL 路径:
?modal-type=gallery-img&gallery-img-id=1
然后我们可以在应用程序的根加载器中访问 URL Search Params:
export const loader: LoaderFunction = ({ request }) => {
const queryParams = new URL(request.url).searchParams;
const modalType = queryParams.get('modal-type');
const galleryImgId = queryParams.get('gallery-img-id');
if (modalType === 'gallery-img') {
if (!galleryImgId) {
console.error(
"must pass the gallery-img-id param if you want to render the 'gallery-img' modal"
);
return null;
}
return {
modalType: 'gallery-img',
galleryImgId,
};
}
return null;
};
然后在应用程序根的渲染函数中,我们检查模态类型和参数。
export default function App() {
const modalProps = useLoaderData() as ModalProps;
return (
<div>
<h1>Modal Example</h1>
<Modal modalProps={modalProps || null} />
<Outlet />
</div>
);
}
在组件内部Modal
,我们可以检查modalType
并根据该值渲染不同的模态框和内容。您可以
使用简单的界面和条件渲染功能注册并创建任意数量的模态框。
优点:
-
模态框是全局的;您可以在根 URL 路径中使用它们
/
,并且可以从应用程序中的任何位置打开它们。 -
易于重构并在应用程序内移动。
它们还支持持久导航,并使用 URL 管理其可见性状态。
缺点:
-
这些模态框不能有自己的加载器,因此它们要么需要在渲染函数(渲染然后获取)中请求数据,要么从父级传递数据作为道具。
-
它们必须使用模态渲染引擎或实用程序在应用程序的根目录中“注册”,从长远来看,这可能更难维护。
-
根目录中的搜索参数会触发所有其他加载器,这可能会导致重新渲染和性能问题。
shouldRevalidate()
如果您不想重新运行加载器,一个潜在的解决方案是引入。
结论
一种模式并不比另一种更好;两者都有优点和缺点,这取决于您的使用情况。
如果您需要您的模态框有自己的加载器,那么请使用嵌套路由方法,但请记住它们不是很灵活且易于重构。
如果您想创建可以从任何地方打开并且更灵活的“全局模式”,则可以使用搜索参数方法;但是,请注意应用程序根中的紧密耦合以及不使用加载器对性能的影响。
致谢:
Jon制作了一个关于如何使用搜索参数创建模态框的视频,并且还提供了一个包含完整示例的存储库。
和往常一样, Remix 团队的Matt总是乐于在 Discord 上回答这类问题。
鏂囩珷鏉ユ簮锛�https://dev.to/infoxicator/modals-with-react-router-6-and-remix-1e35