Abhishek Raj
Abhishek Raj

Reputation: 138

How to create list and detail view using React Router DOM?

I'm new in React and doesn't know how to create List and Detail view using React Router DOM. Some how I created it but I know this is not a good way to do it. If you look into below code, you will find it will always reload the whole page if I select another campaign. What will be the good way to do it. Please help...

App.js

<Switch> 
   <Fragment>
       <PrivateRoute exact path="/admin/campaigns/:id" component={CampaignComponent}/>
       <PrivateRoute exact path="/admin/campaigns/:id/messages" component={CampaignComponent}/>
       <PrivateRoute exact path="/admin/campaigns/:id/contacts" component={CampaignComponent}/>
   </Fragment>
</Switch>

Campaign.js

let layout;

switch (props.match.path) {
      case '/admin/campaigns/:id':
        layout = <CampaignDetailComponent props={props}/>;
        break;

      case '/admin/campaigns/:id/messages':
        layout = <CampaignMessageListComponent props={props}/>;
        break;

      case '/admin/campaigns/:id/contacts':
        layout = <CampaignContactListComponent props={props}/>;
        break;

      default:
        layout = <div/>;
}


return (
     <div>
          <div className="col-6">
            <CampaignListComponent props={props}/>
          </div>
          <div className="col-6">
            {layout}
          </div>
     </div>
)

So, in the first col-6 I want to show the list of Campaigns and in second col-6 I want to render the components as per route change.

You can follow this link to see the actual working code sample that demonstrate this issue.

Upvotes: 1

Views: 6320

Answers (1)

Drew Reese
Drew Reese

Reputation: 203208

Issue

You have your "navigation" coupled to your page UI.

Solution

Split out the rendering of your links in the left section from the content in the right section. The rest of the changes a centered around computing paths and route structuring.

App.js

Render the campaign list container and navigation on its own route. This is so the nested links can inherit from the route path.

import React, { Component } from "react";
import { HashRouter, Redirect, Route, Switch } from "react-router-dom";
import CampaignComponent from "./Campaign/Campaign";
import CampaignListComponent from "./Campaign/Components/CampaignList";
import "./styles.css";

class App extends Component {
  render() {
    return (
      <div className="App">
        <h3>Sample app to demonstrate React Router issue</h3>
        <HashRouter>
          <div className="campaign-container">
            <div className="campaign-list">
              <Route path="/admin/campaigns">
                <CampaignListComponent />
              </Route>
            </div>
            <div className="campaign-detail">
              <Switch>
                <Route path="/admin/campaigns" component={CampaignComponent} />
                <Redirect from="*" to="/admin/campaigns" />
              </Switch>
            </div>
          </div>
        </HashRouter>
      </div>
    );
  }
}

Campaign list component

import React, { useState } from "react";
import { Link, generatePath, useRouteMatch } from "react-router-dom";

const CampaignListComponent = () => {
  const [campaignList, setCampaignList] = useState([
    { id: 1, name: "Campaign1" },
    { id: 2, name: "Campaign2" },
    { id: 3, name: "Campaign3" },
    { id: 4, name: "Campaign4" },
    { id: 5, name: "Campaign5" }
  ]);
  const { url } = useRouteMatch();

  return (
    <div style={{ width: "90%" }}>
      {campaignList.map(({ id, name }) => (
        <div className="campaign-list-item" key={id}>
          <Link to={generatePath(`${url}/:id`, { id })}>{name}</Link>
        </div>
      ))}
    </div>
  );
};

Campaign component

import React from "react";
import { Route, Switch, useRouteMatch } from "react-router-dom";

import CampaignMessageListComponent from "./Components/CampaignMessageList";
import CampaignDetailComponent from "./Components/CampaignDetail";
import CampaignContactListComponent from "./Components/CampaignContactList";

const CampaignComponent = () => {
  const { path } = useRouteMatch();

  return (
    <div className="campaign-list">
      <Switch>
        <Route
          path={`${path}/:id/messages`}
          component={CampaignMessageListComponent}
        />
        <Route
          path={`${path}/:id/contacts`}
          component={CampaignContactListComponent}
        />
        <Route path={`${path}/:id`} component={CampaignDetailComponent} />
      </Switch>
    </div>
  );
};

Content components

Contact

import React from "react";
import { useHistory } from "react-router-dom";

const CampaignContactListComponent = () => {
  const history = useHistory();
  return (
    <div className="row">
      <p>Campaign Contact List</p>
      <button onClick={history.goBack}>Go Back</button>
    </div>
  );
};

Message

import React from "react";
import { useHistory } from "react-router-dom";

const CampaignMessageListComponent = () => {
  const history = useHistory();
  return (
    <div className="row">
      <p>Campaign Message List</p>
      <button onClick={history.goBack}>Go Back</button>
    </div>
  );
};

Detail

import React from "react";
import { Link, useHistory, useParams, useRouteMatch } from "react-router-dom";

const CampaignDetailComponent = () => {
  const { id } = useParams();
  const { url } = useRouteMatch();
  const history = useHistory();

  return (
    <div>
      <h6>Campaign Detail</h6>
      <p>You have selected Campaign - {id}</p>
      <button>
        <Link to={`${url}/messages`}>Goto Messages</Link>
      </button>
      <button>
        <Link to={`${url}/contacts`}>Goto Contacts</Link>
      </button>
      <button onClick={history.goBack}>Go Back</button>
    </div>
  );
};

Edit how-to-create-list-and-detail-view-using-react-router-dom

Upvotes: 5

Related Questions