javascript - 带有路由的 React 中的单页应用程序 - 相同的结构,不同的内容

标签 javascript reactjs

问题

我正在尝试在 React 中使用路由(最好使用 React 钩子(Hook))制作 SPA,但我发现的所有示例和描述都是关于根据 URL 显示不同的组件。我想要的是 Youtube 或 Google 文档之类的东西,其中页面结构/组件(大部分)相同,只有内容发生变化。

上下文

(编辑:添加更多上下文。)

这将是一个文档编辑器/演示器。

页面结构:登录后,顶部总是有一个工具栏(蓝色),用于菜单、通知等。屏幕的其余部分将大致如下两个示例所示:

示例 1: enter image description here 示例 2: enter image description here

搜索 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/

相关文章:

javascript 在 . 之后抓取字符串的一部分。 (点)

javascript - 使用 node.js 或 javascript 在后端读取套接字 API

javascript - 尝试将多个输入与数据列表 ID 同步时出现问题

javascript - 用 setInterval 替换 vue.js 模板

javascript - 在 React 上保持状态

javascript - 如何在reactJS组件文件中编写和调用函数

javascript - 我需要帮助来自动显示 react 类中的方法

javascript - ESLint : Failed to load config "next/babel" to extend from

javascript - 将 google map 自动完成功能与 jquery 自动完成功能集成

reactjs - 一个组件中的多个表单无法识别表单和初始值属性(redux-form v7.0.4)