问题
我正在尝试在 React 中使用路由(最好使用 React 钩子(Hook))制作 SPA,但我发现的所有示例和描述都是关于根据 URL 显示不同的组件。我想要的是 Youtube 或 Google 文档之类的东西,其中页面结构/组件(大部分)相同,只有内容发生变化。
上下文
(编辑:添加更多上下文。)
这将是一个文档编辑器/演示器。
页面结构:登录后,顶部总是有一个工具栏(蓝色),用于菜单、通知等。屏幕的其余部分将大致如下两个示例所示:
搜索 Pane (橙色)可以通过工具栏上的按钮或用户 session 变量打开/关闭。该文档将根据用户 session 变量、URL 中提供的文档 ID 或在搜索 Pane 中选择的文档显示在文档部分(灰色)。
计划的 URL
(在编辑中添加。)
着陆页:/login,登录页。
着陆页:/,此处将显示工具栏和预配置的、基于用户 session 的默认文档。
文档页面:/doc?id=oys2OPkfOwQ,与着陆页相同,但文档部分将包含 ID 作为查询参数提供的文档。
任何其他东西:/something,工具栏和它下面的东西。
想法
(在编辑中添加。)
布局由 CSS 网格定义,页面结构根据变量变化。因此,这将成为来自默认值和用户 session 配置变量的 App 组件的 Prop ,以后可能会更改。
这是我想象的 App 组件的功能(类似伪代码的东西):
<Router>
<Route path='/login'>
<Login/>
// Components: Toolbar and something under it
</Route>
<Route path='/'>
<DocApp/>
// Components: Toolbar, Document or Toolbar, Search and Document
// Default document loaded for default, not logged in user
// Default document loaded from stored user session
</Route>
<Route path='/doc'>
<DocApp/>
// Components: Toolbar, Document or Toolbar, Search and Document
// Same as for '/' except document with ID set as query param is displayed
// This could be called/triggered from search and document component as well
</Route>
<Route path='/somethingelse'>
<SomethingElse/>
</Route>
</Router>
问题
(编辑:改写,最初的问题是如何实现基于 URL 查询参数加载不同文档的解决方案。)
如果有更简单的方法来绘制着陆布局'/' 和特定文档演示者/doc?id=oys2OPkfOwQ 布局,我最感兴趣的是什么?在这两种情况下,都会显示相同的组件,只是提供的参数(提供的文档)不同。
解决方案
(在编辑中添加。)
通过阅读答案和反馈并重新思考我的问题,我意识到我有一个 multiple URLs same content problem .
最佳答案
使用 React Router 渲染基于 UrlParams 的组件。
首先,编辑您的路由以在路由 /doc
下呈现 DocumentLoader
组件
// file: app.js
import React from "react";
import { BrowserRouter, Route } from "react-router-dom";
import DocumentLoader from "./DocumentLoader";
const App = (props) => {
return <BrowserRouter>
<Routes>
<Route path="/doc" element={<DocumentLoader />}>
</Routes>
</BrowserRouter>
}
为加载文档创建自定义 Hook
您需要两个自定义 Hook ,一个用于通过更改 docId 查询参数加载新文档,另一个 Hook 用于监听 docId 更改以从后端重新加载新文档。
注意:编辑 loadDocumentData 以从后端加载
// file: hooks.js
import { useState, useEffect, useCallback } from 'react';
import { useSearchParams } from 'react-router-dom';
/**
* TODO:// Refactor this function to call your backend to get
* Document data by docId
*/
const loadDocumentData = (docId) =>
new Promise((resolve, reject) => {
// this setTimeout for demonstration porpuse only
setTimeout(() => {
resolve({ id: docId, name: `Document name for ${docId}` });
}, 3000);
});
export const useDocument = () => {
const [loading, setLoading] = useState(true);
const { docId, loadDocument } = useDocumentParam();
const [document, setDocument] = useState(null);
useEffect(() => {
setLoading(true);
// Load your document data based on docID
loadDocumentData(docId)
.then((doc) => {
setDocument(doc);
setLoading(false);
})
.catch((e) => {
console.error('Failed to load doc', docId);
});
}, [docId, setLoading]);
return { document, loading, loadDocument };
};
export const useDocumentParam = () => {
const [searchParams, setSearchParams] = useSearchParams();
const docId = searchParams.get('d');
const loadDocument = useCallback(
(newDocId) => {
setSearchParams({ d: newDocId });
},
[setSearchParams]
);
return { docId, loadDocument };
};
创建 DocumentLoader 组件
要监听查询参数的变化,从服务器端加载文档,显示加载指示器并呈现“DocPresenter”组件。
// file: DocumentLoader.js
import * as React from 'react';
import DocPresenter from './DocPresenter';
import { useDocument } from './hooks';
const DocumentLoader = (props) => {
const { loading, document, loadDocument } = useDocument();
if (loading) {
return <div>Display loading indicator while loading the document</div>;
}
return (
<div className="document-container">
<div className="toolbar">NavBar</div>
<div className="searchbox">search component</div>
<div className="editor">
<DocPresenter document={document} setParentstate={loadDocument} />
</div>
</div>
);
};
export default DocumentLoader;
结帐Live Example在 StackBlitz 上。
帮助链接:
关于javascript - 带有路由的 React 中的单页应用程序 - 相同的结构,不同的内容,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73848458/