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 addingchildContextTypesandgetChildContexttoMessageList(the context provider), React passes the information down automatically and any component in the subtree (in this case,Button) can access it by definingcontextTypes.

IfcontextTypesis not defined, thencontextwill 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.

results matching ""

    No results matching ""