我在Web和SO上进行了大量搜索,并在reactiflux聊天中询问,但没有找到一种干净且非骇客的方式来根据路线/路径呈现某些组件。< / p>
假设我有<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
和{{1 }},因此id=xyzfoo
不会显示标题,但标题应该显示。
答案 0 :(得分:5)
为了实现DRY规则(避免代码重复)并根据路由实现条件渲染,您应采用以下结构:
步骤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>
)
}
}
输出:
结论:
因此,每当您希望将标头组件与路由定义的组件一起包括时,请使用<HeaderLayout />
,如果您不想使用标头,则只需使用<Route />
即可在页面中隐藏标头。 / p>
答案 1 :(得分:4)
我认为您看错了这条路。因此,在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>
);
}
现在在每个页面中,您都希望可以使用标题,并在需要时仅介绍Header Component。
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 :(得分:2)
将此有关标头的数据作为路由参数或查询包括在内。
/ projects /:headerBool /:id
或者:
/ projects /:id?header = false
然后您可以通过props.match或props.location访问它。
答案 3 :(得分:1)
您可以先列出所有没有标题的路由,然后在其他交换机中将其他路由分组:
...
<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>
)
没有路径的路由始终匹配。
很遗憾,此解决方案的“未找到”页面可能有问题。它应该放在HeaderRoutes
的末尾,并以Header
呈现。
Dhara's solution没有这个问题。但是,如果React Router内部发生变化,它可能无法与Switch
配合使用:
在
<Switch>
的所有子代应为<Route>
或<Redirect>
元素。 将仅显示与当前位置匹配的第一个孩子。
Route
上的 HOC本身不是Route
。但这应该可以正常工作,因为当前的代码库实际上希望任何React.Element
和<Route>
和<Redirect>
具有相同的props语义。
答案 4 :(得分:0)
您可以使用Route的render属性。 示例:
<Route path='/pageWithoutHeader' render={() => <Page1 />} />
<Route path='pageWithHeader' render={() =>
<Header/>
<Page2 />}
/>
此方法比使用Page内部的标头组件更好。
答案 5 :(得分:0)
一个经常弹出的场景是数据库中的某些页面的Header = false或Page Title = false。 Context是一个很好的解决方案。
import React, { createContext, useContext, useState, useEffect } from "react";
import { Switch, Route } from "react-router-dom";
const AppContext = createContext({});
const Header = () => {
const { headerActive } = useContext(AppContext);
if (!headerActive) {
return null;
}
return (
<header>I'm a header</header>
);
}
const PageWithHeader = () => {
const { setHeaderActive } = useContext(AppContext);
useEffect(() => {
setHeaderActive(true);
},[setHeaderActive]);
return (
<div>Page with header</div>
);
}
const PageWithoutHeader = () => {
const { setHeaderActive } = useContext(AppContext);
useEffect(() => {
setHeaderActive(false);
},[setHeaderActive]);
return (
<div>Page without header</div>
);
}
export const App = () => {
const [headerActive, setHeaderActive] = useState(true);
return (
<AppContext.Provider value={{ headerActive, setHeaderActive }}>
<Header />
<Switch>
<Route path="page-1">
<PageWithHeader />
</Route>
<Route path="page-2">
<PageWithoutHeader />
</Route>
<Switch>
</AppContext.Provider>
);
}
您还可以通过使用自定义钩子进一步简化它。像这样:
export const useHeader = (active) => {
const { headerActive, setHeaderActive } = useContext(AppContext);
useEffect(() => {
if (active !== undefined) {
setHeaderActive(active);
}
},[setHeaderActive, active]);
return headerActive;
}
然后使用原始组件:
const Header = () => {
const headerActive = useHeader();
if (!headerActive) {
return null;
}
return (
<header>I'm a header</header>
);
}
...
const PageWithoutHeader = () => {
useHeader(false);
return (
<div>Page without header</div>
);
}
您还可能会喜欢上使用与useRef配对的useLocation钩子来跟踪先前和当前的路径名,因此您可以具有默认状态而无需在每个页面中声明useHeader。