Правильный способ обработки условного компонента рендера в зависимости от маршрута? - PullRequest
5 голосов
/ 15 марта 2019

Я много искал в Интернете и в SO, спросил в чате activtiflux, но не нашел чистого и нехакерского способа рендеринга некоторых компонентов в зависимости от маршрута / пути.

Допустим, у меня есть <Header />, который должен отображаться на некоторых страницах и должен быть скрыт на других.

Конечно, я могу использовать эту строку в компоненте Header

if (props.location.pathname.indexOf('/pageWithoutAHeader') > -1) return null

Это прекрасно, если /pageWithoutAHeader уникален. Если мне понадобится та же функциональность для 5 страниц, она станет такой:

if (props.location.pathname.indexOf('/pageWithoutAHeader1') > -1) return null
if (props.location.pathname.indexOf('/pageWithoutAHeader2') > -1) return null
if (props.location.pathname.indexOf('/pageWithoutAHeader3') > -1) return null

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

Я считаю, что это может быть даже ошибочно, например, если я не отображаю заголовок для страницы с маршрутом /xyz и у меня есть маршруты с UUID, такими как /projects/:id и id=xyzfoo, поэтому /projects/xyzfoo не показывает заголовок, но должен.

Ответы [ 4 ]

5 голосов
/ 20 марта 2019

Чтобы достигнуть правила СУХОГО (избежать повторения кода) и реализовать условный рендеринг в зависимости от маршрутов, вы должны работать со следующей структурой:

шаг 1) Создать макет (HOC), который возвращает заданныйкомпонент с <Header/> и экспортируйте его

import React from "react"
import { Route } from "react-router-dom"
import Header from "./Header"

export const HeaderLayout = ({component: Component, ...rest}) => (
    <Route {...rest} render={(props) => (
        <>
            <Header/>
            <Component {...props} />
        </>
    )} />
)

Шаг 2) импортируйте макет и используйте его

import React, { Component } from 'react'
import { BrowserRouter, Route, Switch } from "react-router-dom"
import Test1 from './Test1';
import Test2 from './Test2';
import { HeaderLayout } from './HeaderLayout';

export default class Main extends Component {
    render() {
        return (
            <BrowserRouter>
                <Switch>
                    <HeaderLayout path="/test1" component={Test1} />
                    <Route path="/test2" component={Test2}/>
                </Switch>
            </BrowserRouter>

        )
    }
}

Вывод:

path-test1

path-test2

Вывод:

Итак, всякий раз, когда вы хотите включитьКомпонент заголовка вместе с компонентом, заданным для вашего маршрута, используют <HeaderLayout />, а если вы не хотите использовать заголовок, просто используйте <Route />, чтобы скрыть заголовок на вашей странице.

4 голосов
/ 17 марта 2019

Я думаю, что вы смотрите на это неправильно.Так что сочетаемость является чертой номер один, когда мы думаем в React.Заголовок - это повторно используемый компонент, который можно удалить в любом месте!

Мышление таким образом предоставит вам несколько вариантов.

Допустим, у вас есть несколько страничных маршрутов, которые вы разработали для своегоприложение.Заголовок является дочерним компонентом любой из тех страниц, которые его используют!

function AppRouter() {
  return (
    <Router>
      <div>
        <nav>
          <ul>
            <li>
              <Link to="/">Home</Link>
            </li>
            <li>
              <Link to="/about/">About</Link>
            </li>
            <li>
              <Link to="/users/">Users</Link>
            </li>
          </ul>
        </nav>

        <Route path="/" exact component={Index} />
        <Route path="/about/" component={About} />
        <Route path="/users/" component={Users} />
      </div>
    </Router>
  );
}

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

export default function Index(){
    return (
        <React.Fragment>
             <Header/>
             <div> ... Index Content </div>
        </React.Fragment>
    );
}

export default function About(){
    return (
        <React.Fragment>
             //I don't need a header here.
             <div> ... Index Content </div>
        </React.Fragment>
    );
}

Еще более элегантный, но немного более сложный подход - ввести компонент высшего порядка.Это сделает ваши намерения более понятными при добавлении заголовков на уровне маршрута!

function withHeader(Page){
    return class extends React.Component {
        render() {
          // Wraps the input component in a container, without mutating it.
          return (
              <React.Fragment>
                 <Header/>
                 <Page {...this.props} />);
              </React.Fragment>
        }
    }
}

function AppRouter() {
  return (
    <Router>
      <div>
        <nav>
          <ul>
            <li>
              <Link to="/">Home</Link>
            </li>
            <li>
              <Link to="/about/">About</Link>
            </li>
            <li>
              <Link to="/users/">Users</Link>
            </li>
          </ul>
        </nav>

        <Route path="/" exact component={withHeader(Index)} />
        <Route path="/about/" component={About} />
        <Route path="/users/" component={Users} />
      </div>
    </Router>
   );
}
2 голосов
/ 15 марта 2019

Включить эти данные о заголовке в качестве параметра маршрута или запроса.

/ projects /: headerBool /: id

Или:

/ projects /: id? Header = false

Затем вы можете получить к нему доступ через реквизиты.match или props.location.

1 голос
/ 18 марта 2019

Вы можете сначала перечислить все маршруты без заголовка и сгруппировать другие в дополнительный переключатель:

...
<Switch>
  <Route path="/noheader1" ... />
  <Route path="/noheader2" ... />
  <Route path="/noheader3" ... />
  <Route component={HeaderRoutes} />
</Switch>
...

HeaderRoutes = props => (
  <React.Fragment>
    <Header/>
    <Switch>
      <Route path="/withheader1" ... />
      <Route path="/withheader2" ... />
      <Route path="/withheader3" ... />
    </Switch>
  </React.Fragment>
)

Из документации :

Маршруты без пути всегда совпадают.

К сожалению, у этого решения может быть проблема со страницей "not found". Он должен быть помещен в конце HeaderRoutes и будет отображаться с Header.

Решение Дхары не имеет такой проблемы. Но это может не работать с Switch, если внутренние компоненты React Router изменятся:

Все дочерние элементы <Switch> должны быть <Route> или <Redirect> элементами. Будет обработан только первый дочерний элемент, соответствующий текущему местоположению.

HOC over Route - это не Route само по себе. Но это должно работать нормально, потому что текущая кодовая база фактически ожидает любой React.Element с той же семантикой реквизита, что и <Route> и <Redirect>.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...