Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use nested routes to add content to a page without removing the content of the previous route with react-router-v4?

I'm using the sidebar example provided in the official documentation of react-router-v4 as an inspiration https://reacttraining.com/react-router/web/example/sidebar

1- So the initial URL of my application would be : localhost:3000/search-page/Lists

2- I have a list of clickable Links that when clicked display the clicked data on the sidebar, and when this happens the URL gets updated : localhost:3000/search-page/Lists/itemList1selected

3- I then press on the button "Show List number 2" to display a new list

4- my goal is using nested routes, when I click on a Link from "List number 2". it would append it below the item selected from "List number 1" ... and update the URL at the same time : localhost:3000/search-page/Lists/itemList1selected/itemList2selected

here's my current code :

class MyComponent extends Component {

  constructor(props) {
   super(props);
     this.state = {
      showListButtonPressed: true
     };
   this.showTheOtherList = this.ShowTheOtherList.bind(this);
  }

  showTheOtherList() {
   this.setState({
     showListButtonPressed: !this.state.showListButtonPressed
   });
  }

 render() {
   const dataList1 = dataFromDataBase
     .allItemsFromList1()
     .map(list1 => ({
       path: `${this.props.match.params.type}/${list1.id}`,
       mainList1: () => (
        <div>
          {`${list1.company}   ${list1.name}   ${list1.price}`}
        </div>
       ),
       sidebarList1: () => (
         <div>
           <h3>item List 1 selected</h3>
             {list1.company}
         </div>
       )
     }));

   const dataList2 = fakeFlightApiDataTesting
     .allItemsFromList2()
     .map(list2 => ({
       path: `${this.props.match.params.type}/${list2.id}`,
       mainList2: () => (
         <div>
           {`${list2.company} ${list2.name} ${list2.price}`}
         </div>
       ),
       sidebarList2: () => (
         <div>
           <h3>item List 2 selected</h3>
            {list2.company}
         </div>
       )
     }));

    return (
       <div style={{ display: 'flex' }}>
         <div style={{ width: '20%' }}>

           {dataList1.map(route => (
             <div>
               <Route
                 key={route.path}
                 path={`/search-page/${route.path}`}
                 exact={route.exact}
                 component={route.sidebarList1}
               />
             </div>
           ))}

           {dataList2.map(route => (
             <div>
               <Route
                 key={route.path}
                 path={`/search-page/${route.path}`}
                 exact={route.exact}
                 component={route.sidebarList2}
              />
             </div>
           ))}
         </div>
         <div>
           // Switch the lists when button is pressed
           {this.state.showListButtonPressed ? (
             <div>
               <ul>
                 {dataList1.map(route => (
                   <li key={route.path}>
                     <Link to={`/search-page/${route.path}`}>
                       {route.mainList1()}
                     </Link>
                   </li>
                 ))}
               </ul>
                <button onClick={this.showTheOtherList}> 
                  Switch Lists
                </button>
             </div>
           ) : (
             <div>
               <ul>
                 {dataList2.map(route => (
                   <li key={route.path}>
                     <Link to={`/search-page/${route.path}`}>
                       {route.mainList2()}
                     </Link>
                   </li>
                 ))}
               </ul>

               <button onClick={this.showTheOtherList}> 
                 switch Lists 
               </button>
             </div>
           )}
         </div>
       </div>
     );
   }
 }

here are some screenshots to have a better idea :

What I already done and coded This is what I'm trying to achieve

like image 513
AziCode Avatar asked Dec 15 '17 00:12

AziCode


People also ask

How do you handle nested routes in React?

Nested Routes in React Router We will continue working on the User component, because this is the place where we want to have the nested routing via tabs. Therefore, we will instantiate a new set of Link components (which will be our unstyled tabs) which navigate a user to their profile and their account.

What components do we need to create nested routes?

jsx import the required components: import { BrowserRouter as Router, Routes, Route } from "react-router-dom" . It is standard practice to import BrowserRouter as Router because BrowserRouter is too long to type!

What is nested routing?

To recap, nested routes allow you to, at the route level, have a parent component control the rendering of a child component. Twitter's /messages route is the perfect example of this. With React Router, you have two options for creating nested routes.


2 Answers

The difficult part is that since you're using 2 path params, it doesn't function exactly the same as the example they use. Let's say you have "n" list1 options, and "m" list2 options. That means you would n x m total routes. For it to work like this, you would need to create a list2 route combined with every list1 option. Then the path key for list2 would look something like ${this.props.match.params.type}/${list1.id}/${list2.id}. I think this could also be achieved this by making the path for list2 ${this.props.match.url}/${list2.id}, but it would require that the list1 is always selected first.

It's a pretty tricky case, but I hope that helps

like image 153
Trey Huffine Avatar answered Sep 27 '22 00:09

Trey Huffine


You can use different approaches to solve this:

  1. Get another inspiration from official React Router v4 recursive paths.

Using this pattern you can create deeply nested lists, more than 2 levels deep.

  1. Check if there are already 1 item selected at first level in your sidebar and render another <SidebarRoute /> for that case, passing second level seleted item as a prop.

I would suggest you (3rd option) to hold selected items in state, and render accordingly in sidebar. This option will work out just fine, if you are not obligated to send links by e-mail or something, just want to render. Then you can save UI state in the store of your choice (redux, flux, you name it).

Basic example for 2nd approach (idlvl2 is what you are trying to show in sidebar): https://codesandbox.io/s/o53pq1wvz

like image 45
Andy Theos Avatar answered Sep 27 '22 00:09

Andy Theos