资讯专栏INFORMATION COLUMN

react-dnd中context丢失解决方法

Arno / 2873人阅读

摘要:等等这不就用的将的给覆盖了么这也很合理的解释了为啥会报错了。嗯还是拿不到,想起来了,虽然将静态属性拿了出来,但是原型方法不会拿出来啊,所以的就没用了,所以我们需要也将他拿出来,于是,加上一下代码这次总算拿到正确的结果了,开心

踩坑场景

在做业务的时候,有些模块是可以拖动的,恰好这些模块需要从根组件App的context上拿属性,同时App也是作为拖动上下文,被@DragDropContext(HTML5Backend)装饰,当时年少无知,无脑写下了以下代码

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了,赶紧将代码跑起来,结果傻眼了,居然报这个错误

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,懵了,让我想想是咋回事,是不是最后给

  static contextTypes = {
    value: PropTypes.number
  }

给覆盖了原来的Box.contextTypes呀?
不过这也简单,不让他覆盖就好了嘛,于是我写下了如下的代码

Box.contextTypes = Object.assign(Box.contextTypes,{
  value: PropTypes.number
});

真好,报错消失了,大功告成!等等,this.context.value怎么是undefined,拿不到了?我明明在contextTypes里声明了呀,不行,还是得去看一看源码。

React-dnd源码

查看DragSource的源码,可以看到DragSource就是一个普通装饰器包装函数

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这个函数呗

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 (
        
        );
      }
    }

    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就好了,代码如下:

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;

大功告成,我们再来跑一跑。
哇,又报错了,囧,说

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 (
      
    )
  }
}

让我们看看DragDropContext源码

export default function DragDropContext(backendOrModule) {
 ...
 return function decorateContext(DecoratedComponent) {
  ...
   class DragDropContextContainer extends Component {
      getChildContext() {
        return childContext;
      }

      render() {
        return (
           { 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})

这次总算拿到正确的结果了,开心

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/87362.html

相关文章

  • React-dnd实现拖拽,最简单代码,直接可以跑

    摘要:不多说,直接上代码需要版本貌似与方法有关类似的高阶组件包裹被拖的元素高阶组件包裹被释放的元素这个库是必须的,类似于的合成事件解决浏览器差异,抽象事件操作为可以处理的 不多说,直接上代码 react-dnd 需要react版本 > 16.6 ,貌似与react.memo方法有关 import React from react // DragDropContext 类似React的Co...

    xiaokai 评论0 收藏0
  • 使用React DND 完成一个简单的卡片排序功能

    摘要:简介在公司初学其中一个要求让我实现拖拽排序的功能完成之后记录一下实现方法,采用和来实现这个功能。一环境搭建首先,使用脚手架创建一个最基本的项目。 简介 在公司初学react,其中一个要求让我实现拖拽排序的功能,完成之后记录一下实现方法,采用antd和reactDND来实现这个功能。 一、环境搭建 首先,使用 create-react-app 脚手架创建一个最基本的react项目。 np...

    edgardeng 评论0 收藏0
  • React context 丢失问题

    摘要:丢失问题文本是为了说清目前的机制是而不是我们以为的机制,并说明这两者的区别。虽然明白了原理,但是问题并没有解决。上下文注意这里是,需要执行接受回调函数,回调函数中的内容为实测可以成功拿到。 React context 丢失问题 文本是为了说清react context目前的机制是owner context 而不是我们以为的parent context 机制,并说明这两者的区别。...

    Seay 评论0 收藏0
  • 聊一聊我对 React Context 的理解以及应用

    摘要:假如以的作用域链作为类比,组件提供的对象其实就好比一个提供给子组件访问的作用域,而对象的属性可以看成作用域上的活动对象。所以,我借鉴了作用域链的思路,把当成是组件的作用域来使用。 前言 Context被翻译为上下文,在编程领域,这是一个经常会接触到的概念,React中也有。 在React的官方文档中,Context被归类为高级部分(Advanced),属于React的高级API,但官方...

    chengjianhua 评论0 收藏0
  • 【前端面试】作用域和闭包

    摘要:作用域没有块级作用域尽量不要在块中声明变量。只有函数级作用域作用域链自由变量当前作用域没有定义的变量即为自由变量。自由变量会去其父级作用域找。 1. 题目 说一下对变量提升的理解 说明this的几种不同使用场景 创建10个a标签,点击的时候弹出来相应的序号 如何理解作用域 实际开发中闭包的应用 手动实现call apply bind 2. 知识点 2.1 执行上下文 范围:一段scri...

    yanest 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<