简体   繁体   中英

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

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

在此处输入图像描述

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;

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

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 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM