我在使用 react-router@2.4.1
的时候用了 ES6 的 class 来构造 React 组件类,碰到一个坑:
class Example extends React.Component {
constructor(props) {
super(props);
this.context.router; // undefined
this.context.router.push('/'); // error
props.history; // deprecated
}
}
不得已只能用即将被抛弃的props.history.push(...)
来进行路由跳转,这样就相当不优雅了。
其他也有人碰到了这个问题,于是在react-router的GitHub上提了一个Issue #1059
听说要改成这样:
class Example extends React.Component {
constructor(props, context) {
super(props, context);
this.context.router; // it works
}
}
Example.contextTypes = {
router: React.PropsTypes.func.isRequired
}
真是让人感觉非常不优雅,控制台又打出了一行Warning,尽管真的 context.router 出现了。
我要报警了……还是算了,自己踩坑。
contextTypes 是React里面的一个类型验证的机制,用于验证 context 的属性的类型。
React 对 context 的态度是,只要没有在 contextTypes 中声明的属性一律不准加入 context,但没有通过类型验证的只会在控制台打出一行 Warning。
即,如果你不显式声明这个 contextTypes,React Router就没有办法在 context 中加入 router 对象,然后就会出现 this.context.router 未定义的现象。
那么,既然上面的 Example.contextTypes.router 已经声明了,为什么还会报Warning呢?
是因为没有通过contextTypes的验证。
实际上react-router 传进来的这个router对象是一个Object类型,然而react-router却要求是一个 function,自相矛盾。
大概是因为这个 2.4.1
版本自己有坑吧。在新版本与旧版本间狗带了。
那么我们来跳过这个验证:
class Example extends React.Component {
constructor(props, context) {
super(props, context);
this.context.router; // it works
}
}
Example.contextTypes = {
router: Object
}
OK,没有Warning 更没有 Error!
因为它本来就是一个Object嘛!
context 机制是 React 设计用来进行父子组件的通信用的,但是由于 Flux 的出现,父子组件都直接监听Store,改变Store就可以了。这个时候 context 的功能就变得有些鸡肋了。
这我不管,但是 React Router 用了 context,我既然用了 React Router 就要做好了解 context 来填坑的准备。
参考资料: