Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Single Page Application in React with routing - same structure, different content

Problem

I'm trying to make an SPA with routing (ideally with React hooks) in React, but all the examples, descriptions i find are about displaying different components based on the URL. What i want is something like Youtube or Google docs, where the page structure/components are (mostly) the same and only the content changes.

Context

(Edit: adding a bit more context.)

This is going to be a document editor/presenter.

Page structure: after login, there is always a toolbar(blue color) on the top, for menus, notifications, etc. The rest of the screen will be mostly like the two examples below:

Example1: enter image description here Example2: enter image description here

The search pane(orange) could be switched on/off by a button on the toolbar or by a user session variable. The document will be presented in the document section(grey) based on either a user session variable, doc ID provided in URL or selecting a document in the search pane.

Planned URLs

(Added in edit.)

  • Landing page: /login , login page.

  • Landing page: / , here the toolbar and a preconfigured, user session based default doc would be presented.

  • Document page: /doc?id=oys2OPkfOwQ , same as landing page but document section would contain the document with ID provided as query param.

  • Anything else: /something , toolbar and something under it.

Idea

(Added in edit.)

The layout is defined by CSS grid and page structure changes based on a variable. So this is going to be a prop for the App component coming from default value and user session configured variable and could change later.

This is the functionality i imagine for the App component (pseudo code-like thing):

<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>

Question

(Edit: rephrased, original question was how to implement a solution where different documents loaded based on URL query parameter.)

What i'm mostly interested in if there is a simpler way to draw the landing layout '/' and specific doc presenter /doc?id=oys2OPkfOwQ layout? In both cases the same components get displayed, only the provided parameter(doc to present) is different.

Solution

(Added in edit.)

By reading the answers and feedback and re-thinking my problem i realized that i have a multiple URLs same content problem.

like image 230
inspiral Avatar asked Dec 18 '25 16:12

inspiral


1 Answers

Using React Router to render components based on UrlParams.

First of all, edit your routes to render DocumentLoader component under the route /doc

// 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>
}

Create custom hooks for loading documents

You need two custom hooks, one for loading new document by changing the docId query parameter, and another hook to listen to docId changes to reload new document from your backend.

NOTE: Edit loadDocumentData to load from your backend

// 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 };
};


Create DocumentLoader component

To listen on query param changes, load document from server-side, display loading indicator and render the "DocPresenter" component.

// 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;


Checkout Live Example on StackBlitz.

Helper Links:

  • React Router Docs
  • React Custom Hooks Docs
like image 51
David Antoon Avatar answered Dec 21 '25 05:12

David Antoon



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!