概览

Align
2023-08-28
4 min

概览

Item和Types

和Flux一样,react-dnd基于数据来驱动视图。所以当你在屏幕上拖动某个元素时我们不会说拖动了某个元素或者Dom节点,我们将其描述成某种Type下的某个Item正在被拖动。

那么这个Item是什么?其实就是记录了拖动信息的一个javascript对象。例如在一个看板中你拖动一个卡片,这个对象就可能是这样的{ cardId: 42 }。或者在象棋游戏中你拖动了一个棋子,这个对象 可能是这样的{ fromCell: 'C5', piece: 'queen' }。将被拖动的数据描述成对象有利于组件的解耦独立,这种设计的好处我们不久后就能看到。

Type是什么?Type在语法上是一个唯一的javascript字符串或者Symbol,用于将Item分组。例如看板应用中有可拖拽的卡片,也有列表中可拖拽排序的条目,你肯定不希望拖拽卡片经过列表时页面列表产生反应,通过将不同类型的Item分组使react-dnd内部能区分Item类型上的差异并做出正确的反应。

监视器(Monitors)

拖拽是一种有状态的操作。拖拽操作要么在进行中要么就没有。这种状态需要被存储在一个地方,react-dnd通过监听器存储状态并将这些状态暴露出给开发者,开发者可以监听这些状态来处理拖拽逻辑。 你可以通过定义一个collect函数来从监视器中获取拖拽的状态,react-dnd会及时调用你定义的collect函数并将其返回值合并到组件的props中。例如刚刚提到的象棋游戏,现在我们想在棋子被拖动时高亮可放置的单元格并且在棋子hover的位置显示特殊的效果,那么我们需要获取到可被放置的单元格和当前被hover的单元格,那么你的代码可能是这样的:

function collect(monitor) {
  return {
    highlighted: monitor.canDrop(),
    hovered: monitor.isOver()
  }
}

highlightedhovered将会作为props传递给组件。于是我们可以基于这两个prop渲染页面。

接口参数(Connectors)

之前安装react-dnd是安装了两个独立的包,有backend后缀的包是专门处理拖拽逻辑的。分包是为了方便扩展(例如之前提到的用mouseEvent自定义backend),不过这样就会有一个问题,我们使用backend来处理Dom事件,而react组件与其是独立的,使用时也只是在应用外包了一层高阶组件。那么backend内部怎么知道自己应该监听哪些Dom节点,哪些组件是可拖拽的,哪些又是可以放置组件。 这时候我们需要用到Connectors。在collect方法中,实际上Connectors是该方法的第一个参数,通过在方法内部调用collect提供的API来创建一些预定义的拖拽角色(例如拖拽源,拖拽预览和放置对象)。 预定义一个放置对象可以像下面这样:

function collect(connect, monitor) {
  return {
    highlighted: monitor.canDrop(),
    hovered: monitor.isOver(),
    connectDropTarget: connect.dropTarget()//预定义放置目标并通过props返回
  }
}

这样在渲染函数中就可以使用预定义的放置对象:

const render = ()=>{
  const { highlighted, hovered, connectDropTarget } = this.props;

  //该元素将是放置目标且可被`backend`监听
  return connectDropTarget(
    <div className={classSet({
      'Cell': true,
      'Cell--highlighted': highlighted,
      'Cell--hovered': hovered
    })}>
      {this.props.children}
    </div>
  );
}