控制与切换
路由控制与切换
编码控制
使用 router 对象
在基础示例中我们讨论过,传入 Route
配置中的组件会被进行包裹传入go
、push
等可用于手动路由控制的 Props,而对于非路由配置中的组件,React Router 同样提供了便捷的withRouter
,允许以高阶函数的方式将router
对象属性传入组件中。withRouter
会在每次路由跳转时重新渲染被包裹的组件。这里我们编写一个简单的示例,即在组件树的某个组件中显示当前的位置信息。
// 简单的用于显示当前路径信息的组件
class ShowTheLocation extends React.Component {
static propTypes = {
location: PropTypes.object.isRequired
};
render() {
return <div>You are now at {this.props.location.pathname}</div>;
}
}
// 类似于 Redux 中的 connected,将组件链接到 router,并且将 router 的属性以 Props 传入到组件中
const ShowTheLocationWithRouter = withRouter(ShowTheLocation);
每个<Router />
都会将一个 router 对象绑定到 React Context 中,并且方便<Router />
与子级别的<Route>
, <Link>
, <Prompt>
进行通信。虽然这个接口比较方便,不过鉴于其主要是用于内部通信,并且 Context 本身也是实验性的接口,有可能会在 React 未来版本中发生变化,因此我们建议尽量谨慎使用该接口。context.router
对象的属性为:
context.router = {
...history,
match
};
自定义 history 对象
当我们使用 BrowserRouter
、HashRouter
、MemoryRouter
以及 NativeRouter
作为根路由时,它们会自动地创建 history
对象,该对象能够被自动注入到 React 组件中;不过很多时候我们也需要在组件外,譬如状态管理代码中进行些路由控制。此时如果我们需要在组件树之外使用 history
对象,就需要手动创建 history
对象,然后在声明根 Router 对象时引用该对象:
// history.js
import { createBrowserHistory } from 'history'
export default createBrowserHistory()
// index.js
import { Router } from 'react-router-dom';
import history from './history'
ReactDOM.render((
<Router history={history}>
<App />
</Router>
), document.getElementById('root'))
// nav.js
import history from './history'
export default function nav(loc) {
history.push(loc);
}
我们在上文中介绍过 history
中browser history
、hash history
、memory history
的区别,而history
对象还包含以下属性与方法:
length
- (number) 历史记录的数目action
- (string) 当前动作 (PUSH
,REPLACE
, 或者POP
)location
- (object) 当前地址路径,可能包含以下属性pathname
- (string) URL 的路径search
- (string) URL 中查询字符串hash
- (string)URL hash fragmentstate
- (string)push(path, state)
函数传入的state
参数,仅可用于 browser history 或者 memory history
push(path, [state])
- (function) 向当前 history stack 压入新记录replace(path, [state])
- (function) 替换当前 history stack 顶部记录go(n)
- (function) 将 history stack 指针移动n
步goBack()
- (function) 等价于go(-1)
goForward()
- (function) 等价于go(1)
block(prompt)
- (function) 避免跳转
参数传递
转场控制
权限控制
/**
* Returns the pages available when the authentication is complete
* @private
* @returns {XML}
* @constructor
*/
function PrivateRoutes() {
return (
<div>
{/* this div here is worthless */}
<Route exact path="/about" component={About} />
<Route exact path="/" component={Home} />
</div>
);
}
/**
* Returns all the pages that are public and do not require any authentication
* @private
* @returns {XML}
* @constructor
*/
function PublicRoutes() {
return (
<div>
{/* this div here is worthless */}
<Route exact path="/login" component={Login} />
<Route exact path="/contact" component={Contact} />
</div>
);
}
/**
* All the application routes
* @returns {XML}
*/
function Routes({ isAuthenticated }) {
return (
<Provider store={store}>
<Router>
<div>
{/* this div here is worthless */}
<PublicRoutes />
{" "}
{do {
if (isAuthenticated) {
<PrivateRoutes />;
} else {
<Route
path="/"
render={() => <Redirect to={{ pathname: "/login" }} />}
/>;
}
}}
</div>
</Router>
</Provider>
);
}
class App extends Component {
//snip our user stuff from earlier
render() {
return (
<BrowserRouter>
<Match exactly pattern="/" component={HomePage} />
<Match pattern="/login" component={Login} />
<MatchWhenAuthorized pattern="/protected" component={ProtectedPage} />
</BrowserRouter>
);
}
}
const MatchWhenAuthorized = ({ component: Component, ...rest }) => (
<Match
{...rest}
render={renderProps =>
isAuthenticated() ? (
<Component {...renderProps} />
) : (
<Redirect
to={{
pathname: "/login",
state: { from: renderProps.location }
}}
/>
)
}
/>
);