2

I'm working on a React application and I've started trying to implement React Router so that I can swap out certain components on a page based on the URL. I've been searching around and reading React router's own documentation but everything I'm finding seems to either be out of date or not quite what I'm looking for.

This post and this post seem to be close to what I want but I've tried implementing the solution on the first thread without success and the tutorial on React Router's documentation site does seem quite similar to the first solution too but instead focusing on multiple distinct pages rather than one page with components being swapped out.

Hopefully I've no confused anyone with that description but if so then please tell me and I'll try clarify it more.

The common solution to my problem appears to be something along these lines.

Proposed Document Solution:

React Router's docs say that you do this to what appears to be the equivelant to "index.js" based on the const rootElement = document.getElementById("root");. And then add links based on where things are supposed to go.

const rootElement = document.getElementById("root");
render(
  <BrowserRouter>
    <Routes>
      <Route path="/" element={<App />} />
      <Route path="expenses" element={<Expenses />} />
      <Route path="invoices" element={<Invoices />} />
    </Routes>
  </BrowserRouter>,
  rootElement
);

Below is my implementation.

ReactDOM.render(
  <BrowserRouter>
    <Routes>
      <Route path="/" element={<App />} />
      <Route path="view" element={<BookViewPane />} />
      <Route path="add" element={<AddBookPane />} />
    </Routes>
  </BrowserRouter>,
  document.getElementById('root'))

The Issue I have with this:

I've done this to my page and it does work but not how I'd like it to. This is a good way to load a new page with a different component but my React pages have multiple components at work (headers, footers and the like) and this only loads one component at a time. I could create multiple larger components from each of the individual parts for all pages separately but then that multiplies the work and takes away from the point of using react at all.

enter image description here
The App being loaded by defualt like it should. Everything is normal and where it should be. The Link button in the header is a temporary part of my experimenting.

enter image description here
This is the BookViewPane notice how the header is missing.

My Attempt at a Solution

Something I've tried to implement was a switch inside "app.js" that would detect the current URL and load the specific component when it's needed. I cut out anything unneeded.

return(
        <React.Fragment>
            <div>
                <SignIn />
                <BookDetails />
            </div>
            <Header />
            <main>
                <div>
                    <BrowserRouter>
                        <Routes>
                            <Route path={["/", "/main"]} element={<MainPane />}>
                                <Route exact path="/view" element={<BookViewPane />} />
                                <Route exact path="/add" element={<BookAddPane/>} />
                            </Route>
                        </Routes>
                    </BrowserRouter>
            </div>
            </main>
            <Footer />
        
        </React.Fragment>
    );

As well as my index.js file

  ReactDOM.render(
    <BrowserRouter>
      <App />
    </BrowserRouter>,
  document.getElementById('root'))

I'm essentially taking the solution from before but I've not been able to get it to work. I pulled this idea from the above threads and documentation but I clearly don't understand how it's done. Is there something I don't understand, forgot to include or is this even possible at all? Any information would be great!

UPDATE

I've removed the tag from the index.js file and that removed one of the major errors I was receiving. I've started working my way through the other error reports and I'll update with any fixes I find. enter image description here

TeaDrinker
  • 199
  • 1
  • 11

1 Answers1

3

Path must be exact and the {["/main"]} is not required either.

A few things need to be done.

Step One:

<Route path={["/", "/main"]} element={<MainPane />}>

Must be changed to...

<Route exact path="/" element={<MainPane />} />

Without exact keyword Route checks only if begining of location.pathname matches path

Step Two

Remove the <BrowserRouter> wrapper from index.js so that only is left. You cannot render a Router inside another Router so the one in Index.js simply cannot stay.

Step Three

Move the <BrowserRouter> tags that are in app.js so that they are just inside the return parentheses instead of only inside the single <div>. This allows for app.js to recognize React Router as a component.

return(
    <BrowserRouter>
        <React.Fragment>
            <div>
                <SignIn />
                <BookDetails />
            </div>
            <Header />
            <main>
               <div>
                  <Routes>
                     <Route path={["/", "/main"]} element={<MainPane />} />
                     <Route exact path="/view" element={<BookViewPane />} />
                     <Route exact path="/add" element={<BookAddPane/>} />
                  </Routes>      
               </div>
            </main>
            <Footer />
        
        </React.Fragment>
      </BrowserRouter>
    );

After this, the URLs should work as intended.

As an aside. Make sure that you're running React 18 to ensure full compatibility. Check here for info on updating a project.

TeaDrinker
  • 199
  • 1
  • 11
Kadet
  • 1,344
  • 3
  • 10
  • I mad the adjustment but it made no noticeable difference. It's very possibly part of the problem but not all of it it seems. I realized I forgot to include the details of my index.js file so I added it to the end of the original question. – TeaDrinker May 07 '22 at 17:33
  • Am I supposed to use the tag for both or only use it in the App.js file. I tried swapping it our for in the index.js file and nothing changed. – TeaDrinker May 07 '22 at 17:36
  • Note. I noticed in the stack trace that there was a line saying "Router cannot be rendered inside another router." I removed the tags from index.js and that error went away.
    I've added a screenshot of the remaining stack traces and I'm going to start working my way through them. Thanks for the help!
    – TeaDrinker May 07 '22 at 17:45
  • You use React18, so you must use proper syntax in index.js file https://reactjs.org/blog/2022/03/08/react-18-upgrade-guide.html and a blog with example relevant to React18 https://bobbyhadz.com/blog/react-reacdom-render-no-longer-supported-in-react-18 – Kadet May 07 '22 at 17:51
  • Another useful link: https://stackoverflow.com/questions/71668256/deprecation-notice-reactdom-render-is-no-longer-supported-in-react-18 – Kadet May 07 '22 at 18:07
  • I made the changes and when I have the application run **React.Version** it prints 18.1.0. So that's taken care of. I also spent some time and fixed the `useHref() may be used only in the context of a component` error. That was a fairly simple fix. All I needed to do was wrap the entire app rather than just the routes themselves in the BrowserRouter tags and that issue went away. Hopefully it stays away. – TeaDrinker May 07 '22 at 18:17
  • I solved it. Your original answer was very close. The last error in the stack-trace that I was dealing with was `TypeError: meta.relativePath.startsWith is not a function` After some digging, I came across [this post](TypeError: meta.relativePath.startsWith is not a function) and it was answered with one other change to the route. I changed it from `}>` to `} />` and removed the **** closing tag from the end. This immediately solved the issue and everything seems to be working correctly – TeaDrinker May 07 '22 at 18:33
  • If it's fine with you, I was going to edit your original answer with the final solution and give you the credit for your help. You can do it too if you like. – TeaDrinker May 07 '22 at 18:34
  • Feel free to improve my answer. – Kadet May 07 '22 at 18:42