在作業務的時候,有些模塊是能夠拖動的,剛好這些模塊須要從根組件App的context上拿屬性,同時App也是做爲拖動上下文,被@DragDropContext(HTML5Backend)裝飾,當時年少無知,無腦寫下了如下代碼javascript
const boxSource = { canDrag(props, monitor) { ... }, beginDrag(props) { ... }, endDrag(props, monitor) { ... }, }; @DragSource('box', boxSource, (connect, monitor) => ({ connectDragSource: connect.dragSource(), isDragging: monitor.isDragging(), })) export default class Box extends Component { static contextTypes = { value: PropTypes.number }; static propTypes = { ... } render() { const { isDragging, connectDragSource, src } = this.props; const { value } = this.context; return ( connectDragSource( ... ) ); } }
美滋滋啊,美滋滋啊,so ez,會用react-dnd了,趕忙將代碼跑起來,結果傻眼了,竟然報這個錯誤html
Invariant Violation: Could not find the drag and drop manager in the context of Box. Make sure to wrap the top-level component of your app with DragDropContext. Read more: http://react-dnd.github.io/react-dnd/docs-troubleshooting.html#could-not-find-the-drag-and-drop-manager-in-the-context
提示咱們在拖拽組件Box的context上找不到react-dnd須要的drag and drop manager,懵了,讓我想一想是咋回事,是否是最後給java
static contextTypes = { value: PropTypes.number }
給覆蓋了原來的Box.contextTypes
呀?
不過這也簡單,不讓他覆蓋就行了嘛,因而我寫下了以下的代碼react
Box.contextTypes = Object.assign(Box.contextTypes,{ value: PropTypes.number });
真好,報錯消失了,大功告成!等等,this.context.value怎麼是undefined
,拿不到了?我明明在contextTypes裏聲明瞭呀,不行,仍是得去看一看源碼。git
查看DragSource的源碼,能夠看到DragSource就是一個普通裝飾器包裝函數github
function DragSource(type, spec, collect, options = {}) { ... return function decorateSource(DecoratedComponent) { return decorateHandler({ connectBackend: (backend, sourceId) => backend.connectDragSource(sourceId), containerDisplayName: 'DragSource', createHandler: createSource, registerHandler: registerSource, createMonitor: createSourceMonitor, createConnector: createSourceConnector, DecoratedComponent, getType, collect, options, }); }; }
那咱們繼續去看一看 decorateHandler
這個函數唄app
export default function decorateHandler({ DecoratedComponent, createHandler, createMonitor, createConnector, registerHandler, containerDisplayName, getType, collect, options, }) { ... class DragDropContainer extends Component { ... static contextTypes = { dragDropManager: PropTypes.object.isRequired, } ... render() { return ( <DecoratedComponent {...this.props} {...this.state} ref={this.handleChildRef} /> ); } } return hoistStatics(DragDropContainer, DecoratedComponent); }
嗯, decorateHandler
就是一個HOC生成函數嘛,hoistStatics
就是hoist-non-react-statics
這個庫,作過HOC的童鞋必定不陌生,他就是將WrappedComponent的靜態方法和靜態屬性提到HOC上,面,避免WrappedComponent的靜態屬性和靜態方法丟失了,看似挺合理,嗯嗯。等等!這不就用WrappedComponent的contextTypes將HOC的contextTypes給覆蓋了麼?這也很合理的解釋了爲啥會報錯了。函數
知道了其中的原來,那咱們就讓HOC和WrappedComponent各自保留一份contextTypes好了,首先咱們須要用另外一個變量來保留對WrappedComponent的引用,由於被@DragSource
裝飾後,WrappedComponent的變量名就會被HOC覆蓋了,而後咱們再對WrappedComponent加上contextTypes就行了,代碼以下:ui
class Box extends Component { static propTypes = { connectDragSource: PropTypes.func.isRequired, ... } render() { const { isDragging, connectDragSource, src } = this.props; const { value } = this.context; ... return ( connectDragSource( ... ) ); } } const Temp = Box; const Box1 = DragSource('box', boxSource, (connect, monitor) => ({ connectDragSource: connect.dragSource(), isDragging: monitor.isDragging(), }))(Box); Temp.contextTypes = { value: PropTypes.number, } export default Box1;
大功告成,咱們再來跑一跑。
哇,又報錯了,囧,說this
Invariant Violation: App.getChildContext(): childContextTypes must be defined in order to use getChildContext().
好,那咱們來看看根組件咋回事,我寫的根組件以下
@DragDropContext(HTML5Backend) class App extends React.Component { constructor(props) { super(props); } static childContextTypes = { value:PropTypes.number, } getChildContext(){ return { value:1 } } render() { return ( <Box /> ) } }
讓咱們看看DragDropContext
源碼
export default function DragDropContext(backendOrModule) { ... return function decorateContext(DecoratedComponent) { ... class DragDropContextContainer extends Component { getChildContext() { return childContext; } render() { return ( <DecoratedComponent {...this.props} ref={(child) => { this.child = child; }} /> ); } } return hoistStatics(DragDropContextContainer, DecoratedComponent); }; }
得,又是HOC的問題,可是有點不一樣,就是contextTypes必定要準確設置在須要的組件上,可是childContextTypes只要放在上層組件就能夠了,因此我作了以下修改:
class App
中的static childContextType = { value: PropTypes.number }
App.childContextTypes = Object.assign(App.childContextTypes,{ value: PropTypes.number });
此次總該行了吧,心累啊。嗯?仍是拿不到this.context.value
,想起來了!,雖然hoist-non-react-statics
將靜態屬性拿了出來,可是原型方法不會拿出來啊,因此WrappedComponent的getChildContext
就沒用了,因此咱們須要也將他拿出來,因而,加上一下代碼
const temp = {...App.prototype.getChildContext()}; App.prototype.getChildContext = () => ({...temp, value:1})
此次總算拿到正確的結果了,開心