利用React的高阶组件对本来的<AppLayout />举办改革,将其转变为一个独立通用的组件。对付本来的<AppLayout />,可以利用这个SlotProvider高阶组件,转换成一个具备插槽分发本领的组件。
import { SlotProvider } from './SlotProvider.js' class AppLayout extends React.Component { static displayName = 'AppLayout' render () { return ( <div> <header> <Slot></Slot> </header> <main> <Slot></Slot> </main> <footer> <Slot></Slot> </footer> </div> ) } } export default SlotProvider(AppLayout)
通过以上的经验,可以看到,当设计开拓一个组件时,
组件大概需要由一个根组件和多个子组件一起相助来完成组件成果。好比插槽分发组件实际上需要SlotProvider与<Slot />和<AddOn />一起共同利用,SlotProvider作为根组件,而<Slot />和<AddOn />都算是子组件。
子组件相对付根组件的位置可能子组件之间的位置是不确定。对付SlotProvider而言,<Slot />的位置是不确定的,它会处在被SlotProvider这个高阶组件所包裹的组件的模板的任何位置,而对付<Slot />和<AddOn />,他们直接的位置也不确定,一个在SlotProvider包装的组件的内部,另一个是SlotProvider的children。
子组件之间需要依赖一些全局态的API可能数据,好比<Slot />实际渲染的内容来自于SlotProvider收集到的<AddOn />的内容。
这时我们就需要借助一其中间者作为前言来共享数据,对比特别引入redux这些第三方模块,直接利用Context可以更优雅。
实验一下新版本的Context API利用新版的Context API对之前的插槽分发组件举办改革。
// SlotProvider.js function getDisplayName (component) { return component.displayName || component.name || 'component' } export const SlotContext = React.createContext({ requestAddOnRenderer: () => {} }) const slotProviderHoC = (WrappedComponent) => { return class extends React.Component { static displayName = `SlotProvider(${getDisplayName(WrappedComponent)})` // 用于缓存每个<AddOn />的内容 addOnRenderers = {} requestAddOnRenderer = (name) => { if (!this.addOnRenderers[name]) { return undefined } return () => ( this.addOnRenderers[name] ) } render () { const { children, ...restProps } = this.props if (children) { // 以k-v的方法缓存<AddOn />的内容 const arr = React.Children.toArray(children) const nameChecked = [] this.addOnRenderers = {} arr.forEach(item => { const itemType = item.type if (item.type.displayName === 'AddOn') { const slotName = item.props.slot || '$$default' // 确保内容独一性 if (nameChecked.findIndex(item => item === stubName) !== -1) { throw new Error(`Slot(${slotName}) has been occupied`) } this.addOnRenderers[stubName] = item.props.children nameChecked.push(stubName) } }) } return ( <SlotContext.Provider value={ requestAddOnRenderer: this.requestAddOnRenderer }> <WrappedComponent {...restProps} /> </SlotContext.Provider> ) } } } export const SlotProvider = slotProviderHoC
移除了之前的childContextTypes和getChildContext(),除结局部的调解,整体焦点的对象没有大变革。
// Slot.js import { SlotContext } from './SlotProvider.js' const Slot = ({ name, children }) => { return ( <SlotContext.Consumer> {(context) => { const addOnRenderer = requestAddOnRenderer(name) return (addOnRenderer && addOnRenderer()) || children || null }} </SlotContext.Consumer> ) } Slot.displayName = 'Slot' Slot.propTypes = { name: PropTypes.string } Slot.defaultProps = { name: '$$default' }
由于之前就凭据出产者消费者的模式来利用Context,加上组件自身也较量简朴,因此利用新的API举办改革后,不同不大。
总结对比props和state,React的Context可以实现跨层级的组件通信。
Context API的利用基于出产者消费者模式。出产者一方,通过组件静态属性childContextTypes声明,然后通过实例要领getChildContext()建设Context工具。消费者一方,通过组件静态属性contextTypes申请要用到的Context属性,然后通过实例的context会见Context的属性。
利用Context需要多一些思考,不发起在App中利用Context,但假如开拓组件进程中可以确保组件的内聚性,可控可维护,不粉碎组件树的依赖干系,影响范畴小,可以思量利用Context办理一些问题。
通过Context袒露API或者在必然水平上给办理一些问题带来便利,但小我私家认为不是一个很好的实践,需要慎重。
旧版本的Context的更新需要依赖setState(),是不行靠的,不外这个问题在新版的API中得以办理。