İlker
İlker

Reputation: 2110

React-router-dom nesting doesn't work in children components

For visualisation and clear picture, I will show the app's screen shot first.

enter image description here

I'm using github api to fetch Repos and Users. Simple. Now this how App.tsx looks;

// App.tsx
import React from "react";
import MainSearchPage from "./Components/MainSearchPage";
import Header from "./Components/Header";
import "./Sass/main.scss";

const App: React.FC = () => {
  return (
    <div className="App">
      <Header />
      <MainSearchPage />
    </div>
  );
};

export default App;

This is the MainSearchPage component;

// MainSearchPage.tsx
import React, { useContext } from "react";
import { GithubContext } from "../Context/GithubContext";
import searchPc from "../Icons/search-pc.svg";
import SearchResults from "./SearchResults";

const MainSearchPage: React.FC = () => {
  const { isSearched } = useContext(GithubContext);
  return (
    <div>
      {isSearched ? (
        <SearchResults />
      ) : (
        <div className="blank-search-page">
          <div className="blank-search-page__items">
            <img src={searchPc} alt="search-pc" />
            <p>Search results will appear here</p>
          </div>
        </div>
      )}
    </div>
  );
};

export default MainSearchPage;

When input changes, isSearch becomes true and SearchResults is rendering. This is the SearchResults, where the problem begins.

//SearchResults.tsx
import React from "react";
import { Route, BrowserRouter as Router, Switch } from "react-router-dom";
import MainSearchResults from "./MainSearchResults";
import SideBarSearchResults from "./SideBarSearchResults";
import UsersPage from "./UsersPage";

const SearchResults = () => {
  return (
    <div className="search-results">
      <Router>
        <SideBarSearchResults />
        <Switch>
          <Route path="/" component={MainSearchResults} />
          <Route path="/search-users" component={UsersPage} />
        </Switch>
      </Router>
    </div>
  );
};

export default SearchResults;

So MainSearchResults is basically repo results on the right. Sidebar is obvious I suppose. And UsersPage is the component that should be rendered when I click the Users on the left. But nothing happens when I click. Url changes and that's it. No errors in the console or anything. It was working before I put "isSearched" state. But I don't think it should matter.

SideBar component;

//SideBarSearchResults.tsx
import React, { useContext } from "react";
import repositoriesSVG from "../Icons/repositories.svg";
import usersSVG from "../Icons/users.svg";
import bookmarkblackSVG from "../Icons/bookmarkblack.svg";
import { Link } from "react-router-dom";
import { GithubContext } from "../Context/GithubContext";
const SideBarSearchResults: React.FC = () => {
  const { repoCount, userCount } = useContext(GithubContext);
  return (
    <div className="side-bar-search-results">
      <Link to="/">
        <div className="side-bar-search-results__repositories">
          <img src={repositoriesSVG} alt="repo" />
          <p className="result-title">Repositories</p>
          <p className="quantity">{repoCount ?? 0}</p>
        </div>
      </Link>
      <Link to="/search-users">
        <div className="side-bar-search-results__users">
          <img src={usersSVG} alt="users" />
          <p className="result-title">Users</p>
          <p className="quantity">{userCount ?? 0}</p>
        </div>
      </Link>
      <div className="side-bar-search-results__bookmarked ">
        <img src={bookmarkblackSVG} alt="bookmarked" />
        <Link to="/repo-details">
          <p className="result-title">Bookmarked</p>
        </Link>
        <p className="quantity">15</p>
      </div>
    </div>
  );
};

export default SideBarSearchResults;

Upvotes: 2

Views: 187

Answers (2)

shiponcs
shiponcs

Reputation: 1687

As a complement to Drew Reese's anwer answer:

When you use any route matching component you need to know how it works so that you may go for another library. In case react-router <Router/>, it doesn't render a component exclusively by default. Meaning, your application could possibly end up matching two paths for one route and renders the first one so order does matter. For example, if you want to route '/products', it matches both / and /products. That was your case. How to solve this:

  • use exact or,
  • reorder the routes, what Drew Reese just mentioned in his answer.

Alternative:

What if you don't like this path matching of react-router? Here you have Reach Router whose path matching is a bit different. Reach Router ranks the paths and renders the one that makes the most sense. You can read about their path ranking. So, you don't need to think about the orders or using an extra keyword exact because reach router itself prioritizes the router using the path ranking**system.

Upvotes: 1

Drew Reese
Drew Reese

Reputation: 203587

Issue

The Switch component matches and renders the first matched path, and "/" is a prefix for every path. In other words, it will always be the path matched and returned first.

Solution

Reorder your Routes to specify the more specific paths first, and go in descending order of path specificity.

<Router>
  <SideBarSearchResults />
  <Switch>
    <Route path="/search-users" component={UsersPage} />
    <Route path="/" component={MainSearchResults} />
  </Switch>
</Router>

Upvotes: 1

Related Questions