Как определить согласованный маршрут от компонента за пределами компонент, который был сопоставлен с помощью реактивного маршрутизатора? - PullRequest
1 голос
/ 05 мая 2020

У меня есть следующая структура в моем приложении React, используя react-router-dom.

<Router>
  <Header/>
  <Main>
    <AllRoutes>      // THIS HANDLES THE SWITCH WITH ALL THE ROUTES
      <Switch>
        <Route exact path={ROUTES.HOME} component={Home}/>
        <Route exact path={ROUTES.ABOUT} component={About}/>
        <Route exact path={ROUTES.PRIVACY} component={Privacy}/>
        // ETC
      </Switch>
    </AllRoutes>
  </Main>
  <Footer/>         // <==== FOOTER NEEDS TO KNOW WHICH ROUTE HAS BEEN MATCH
<Router>

ВОПРОС

Нижний колонтитул должен знать, что <Route/> был совпадением. Каков наилучший шаблон для этого?


ВАРИАНТ # 1

Я обнаружил, что useRouteMatch перехватывает реагирует на документы маршрутизатора :

enter image description here

enter image description here

Это вроде бы сработает, но я не думаю это достаточно хорошо для моей ситуации. Потому что строка URL-адреса может соответствовать маршруту и ​​в то же время не быть действительным маршрутом.

Например:

  • Маршрут: /:language/privacy
  • Действительный маршрут: /en/privacy
  • Недействительный маршрут, который также может совпадать: /notALanguage/privacy

После того, как маршрут имеет совпадение, мне обычно нужно проверить, действителен ли он перед рендерингом страница компонента или страница 404.

Например:

<Route exact path={"/:language/privacy"} render={(routeProps) => {
 const possibleLanguage = routeProps.match.params.language;
 if (possibleLanguage in LANGUAGES) {
   return(
     <PrivacyPage lang={possibleLanguage}/>
   );
 }
 else {
  return(
    <Page404/>
  );
 }
}}/>

ВАРИАНТ № 2

Я думаю о следующем:

  • App.js звонки useLocation. Таким образом, он всегда выполняет повторную визуализацию при изменении маршрута.
  • Я мог бы добавить функцию detectRoute в App.js, чтобы заранее выполнить всю проверку маршрута.
  • И мой AllRoutes компоненту не нужен компонент. Я бы реализовал собственный переключатель JS и отобразил соответствующий маршрут.
  • Таким образом, я заранее знаю, какой <Route/> будет соответствовать, и могу передать его <Footer/> или любому компоненту, который находится вне из совпавших <Route/>.

Примерно так:

SandBox Link

export default function App() {
  console.log("Rendering App...");

  const location = useLocation();

  // THIS WOULD BE THE detectRoute FUNCTION
  // I COULD EVEN USE THE useRouteMatch HOOK IN HERE
  const matchedRoute =
    location.pathname === ROUTE1
      ? "ROUTE1"
      : location.pathname === ROUTE2
      ? "ROUTE2"
      : "404";

  return (
    <div>
      <div className="App">
        <Link to={ROUTE1}>Route 1</Link>
        <Link to={ROUTE2}>Route 2</Link>
        <Link to={"/whatever"}>Route 404</Link>
      </div>
      <div>
        <AllRoutes matchedRoute={matchedRoute} />
      </div>
    </div>
  );
}

function AllRoutes(props) {
  switch (props.matchedRoute) {
    case "ROUTE1":
      return <Route exact path={ROUTE1} component={Page1} />;
    case "ROUTE2":
      return <Route exact path={ROUTE2} component={Page2} />;
    default:
      return <Route exact path={"*"} component={Page404} />;
  }
}

Работает. Но я хотел бы знать, есть ли правильный способ сделать это, потому что это кажется немного странным и может быть что-то специально разработанное для этого.

Ответы [ 2 ]

1 голос
/ 05 мая 2020

Обычно вы хотите:

  1. Объединить компоненты вместе
  2. Создать другой переключатель для их маршрутизации (и передать параметры соответствия)

I составить несколько исчерпывающий пример вариантов. Надеюсь, это поможет!

import React from "react";
import "./styles.css";
import { Switch, Link, Route, BrowserRouter } from "react-router-dom";

const hoc = (Component, value) => () => (
  <>
    <main>
      <Component />
    </main>
    <Footer value={value} />
  </>
);

const Wrapper = ({ component: Component, value }) => (
  <>
    <main>
      <Component />
    </main>
    <Footer value={value} />
  </>
);

const WrapperRoute = ({ component, value, ...other }) => (
  <Route
    {...other}
    render={props => <Wrapper component={component} value={value} {...props} />}
  />
);

const Footer = ({ value }) => <footer>Footer! {value}</footer>;
const Header = () => <header>Header!</header>;

const Another = () => <Link to="/onemore">One More!</Link>;
const Home = () => <Link to="/other">Other!</Link>;
const OneMore = () => <Link to="/">Home!</Link>;
const Other = () => <Link to="/another">Another!</Link>;

export default () => (
  <BrowserRouter>
    <Header />
    <Switch>
      {/* You could inline it! */}
      <Route
        path="/another"
        render={() => (
          <>
            <main>
              <Another />
            </main>
            <Footer value="" />
          </>
        )}
      />
      {/* You could use a custom route component (that uses an HOC or a wrapper) */}
      <WrapperRoute
        component={OneMore}
        path="/onemore"
        value="I got one more!"
      />
      {/* You could use a Higher-Order Component! */}
      <Route path="/other" component={hoc(Other, "I got other!")} />
      {/* You could use a wrapper component! */}
      <Route
        path="/"
        render={() => <Wrapper component={Home} value="I got home!" />}
      />
    </Switch>
    {/* You could have another switch for your footer (inline or within the component) */}
    <Switch>
      <Route
        path="/another"
        render={() => <Footer value="Switch footer another!" />}
      />
      <Route
        path="/other"
        render={() => <Footer value="Switch footer other!" />}
      />
      <Route
        path="/onemore"
        render={() => <Footer value="Switch footer onemore!" />}
      />
      <Route path="/" render={() => <Footer value="Switch footer home!" />} />
    </Switch>
  </BrowserRouter>
);

Обратите внимание, что WrapperRoute позволит вам выполнить проверку параметров соответствия перед их передачей. При необходимости вы можете набрать Redirect.

0 голосов
/ 06 мая 2020

Что я в итоге сделал:

Поскольку я использую Redux, я добавил часть глобального состояния для отслеживания согласованного маршрута.

И я отправляю действия в обновить это состояние из свойства render из компонента <Route/>.

<Switch>
  <Route key={index} exact path={"/some-route"} render={(routeProps) => {

    // HERE I DISPATCH AN ACTION TO CHANGE THE STATE FOR THE CURRENT ROUTE

    dispatch({
      type: UPDATE_CURRENT_ROUTE,
      payload: { name: "SOME_ROUTE_NAME" }
    });

    return (
      <PrivacyPage
        {...routeProps}
      />
    );

  }}/>
</Switch>

И теперь я могу сделать это с нижним колонтитулом. js:

function Footer() {

  const currentRoute = useSelector((state) => state.currentRoute);

  // RENDER FOOTER ACCORDINGLY TO THE CURRENT ROUTE
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...