Implicitly Passing The Store Down
在上一节,我们为了使store可以顺着data-flow传递至每一个需要用到store的组件里,我们使用显式传递props的方法。我们可以注意这样的方法依然很dirty, 我们手写了大量的props,尽管他们都指代store。我们不妨看看store是如何传递至FilterLink的:
TodoApp => Footer => FilterLink
Footer组件是一个Presentational Component,但它包含了Container,由于Container的职责包含处理behavior,所以它必须能够接触到store. 为了让它接触到store,我们让Footer传入了store参数,这仅仅是一种媒介的做法。 我们始终想要排除这种做法,现在我们将集中精力来解决它。
Context
在React中有一个高级特性叫做context,具体见官方文档. 在文档里的例子中,我们注意有这么一段话:
By adding
childContextTypes
andgetChildContext
toMessageList
(the context provider), React passes the information down automatically and any component in the subtree (in this case,Button
) can access it by definingcontextTypes
.If
contextTypes
is not defined, thencontext
will be an empty object.
注意标粗的文字,我们可以发现使用context
可以完美的解决现有的问题:
- 创建一个组件,调用
getChildContext
,指定所有子组件可以通过context
获取的属性 - 仅在需要此属性的组件上定义
contextTypes
按照以上步骤,我们创建一个组件叫Provider: (请不要问我为什么叫这个名字 : ) )
class Provider extends Component {
getChildContext() {
return {
store: this.props.store
}
}
render() {
return this.props.children
}
}
// we will use context so we have to do this.
Provider.childContextTypes = {
store: React.PropTypes.object
}
用它包裹TodoApp:
ReactDOM.render(
<Provider store={createStore(todoApp)}>
<TodoApp />
</Provider>,
document.getElementById('root')
)
给需要store的子组件们添加contextTypes:
// The second argument is context
const AddTodo = (props, { store }) => {
// ...
}
AddTodo.contextTypes = {
store: React.PropTypes.object
}
// In VisibleTodoList and FilterLink
componentDidMount() {
const { store } = this.context
this.unsubscribe = store.subscribe(() =>
this.forceUpdate()
)
}
render() {
const { store } = this.context
const state = store.getState()
// ...
}
// Outside the class
VisibleTodoList.contextTypes = {
store: React.PropTypes.object
}
FilterLink.contextTypes = {
store: React.PropTypes.object
}
移除之前不必要的”媒介“参数传递:
const Footer = () => (
<p>
Show:
{' '}
<FilterLink
filter='SHOW_ALL'
>
All
</FilterLink>
...
</p>
)
const TodoApp = () => (
<div>
<AddTodo />
<VisibleTodoList />
<Footer />
</div>
)
done!
但是context并不是官方推荐的做法,它具有不稳定性,并且可能在将来的版本迭代中被移除,见官方文档Why Not To Use Context.
下一节我们将使用redux提供的Provider.