6

我正在尝试使用 Rails5、动作 Cable、React 和 Rails 以及 React DnD 制作 POC。

目的是制作像 trello 这样的应用程序,但用于招聘过程。

我的前面是 ReactJS。

我有 3 个组件,首先,容器调用“Candidates”,该组件调用 2 个“CardBoard”组件,这些组件调用“Card”组件。

我用户对可拖动卡片和可放置 CardBoard 的 DnD 库做出反应。当我将卡片放在纸板上时,我使用 post call 和 websocket(来自 rails5 的操作电缆)来更新我的状态。我不明白为什么我在电话后收到此消息:

Uncaught Error: Cannot have two HTML5 backends at the same time.
    at HTML5Backend.setup (eval at <anonymous> (webpack-bundle.self-7b1a342….js?body=1:4175), <anonymous>:87:15)
    at DragDropManager.handleRefCountChange (eval at <anonymous> (webpack-bundle.self-7b1a342….js?body=1:3566), <anonymous>:52:22)
    at Object.dispatch (eval at <anonymous> (webpack-bundle.self-7b1a342….js?body=1:4931), <anonymous>:186:19)
    at HandlerRegistry.addSource (eval at <anonymous> (webpack-bundle.self-7b1a342….js?body=1:3594), <anonymous>:104:18)
    at registerSource (eval at <anonymous> (webpack-bundle.self-7b1a342….js?body=1:4294), <anonymous>:9:27)
    at DragDropContainer.receiveType (eval at <anonymous> (webpack-bundle.self-7b1a342….js?body=1:1793), <anonymous>:146:32)
    at DragDropContainer.receiveProps (eval at <anonymous> (webpack-bundle.self-7b1a342….js?body=1:1793), <anonymous>:135:14)
    at new DragDropContainer (eval at <anonymous> (webpack-bundle.self-7b1a342….js?body=1:1793), <anonymous>:102:13)
    at eval (eval at <anonymous> (webpack-bundle.self-7b1a342….js?body=1:4399), <anonymous>:295:18)
    at measureLifeCyclePerf (eval at <anonymous> (webpack-bundle.self-7b1a342….js?body=1:4399), <anonymous>:75:12)

候选人.jsx =

import React, { PropTypes } from 'react';
import { DragDropContextProvider } from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend';
import CardBoard from './CardBoard.jsx';

export default class Candidates extends React.Component {

  constructor(props, _railsContext) {
    super(props);

    this.state = {
      candidates: this.props.candidates
    }

    this.filterByStatus = this.filterByStatus.bind(this)
  }

  componentDidMount() {
    this.setupSubscription();
  }

  setupSubscription() {
    App.candidate = App.cable.subscriptions.create("CandidateChannel", {
      connected: () => {
        console.log("User connected !")
      },

      received: (data) => {
        this.setState({ candidates: data.candidates })
      },
    });
   }

  render() {
    return (
      <DragDropContextProvider backend={HTML5Backend}>
        <div className="recruitment">
          {
            ["À Rencontrer", "Entretien"].map((status, index) => {
              return (
                <CardBoard candidates={(this.filterByStatus({status}))} status={status} key={index} />
              );
            })
          }
        </div>
      </DragDropContextProvider>
    );
  } 
}

CardBoard.jsx =

import React, { PropTypes } from 'react';
import Card from './Card.jsx';
import { DropTarget } from 'react-dnd';
import ItemTypes from './ItemTypes';

const cardTarget = {
  drop(props: Props) {
    var status = ''

    if(props.status == "À Rencontrer") {
      status = 'to_book'
    } else {
      status = 'interview'
    }

    return { status: status };
  },
};

@DropTarget(ItemTypes.CARD, cardTarget, (connect, monitor) => ({
  connectDropTarget: connect.dropTarget(),
  isOver: monitor.isOver(),
  canDrop: monitor.canDrop(),
}))

export default class CardBoard extends React.Component<Props> {

  constructor(props, _railsContext) {
    super(props);
  }

  render() {
    const { canDrop, isOver, connectDropTarget } = this.props;
    const isActive = canDrop && isOver;


    return connectDropTarget(
      <div className={`${this.props.status} ui cards`}>
        <h2>{`${this.props.status} (${this.props.candidates.length})`}</h2>
        {
          (this.props.candidates).map((candidate, index) => {
            return <Card candidate={candidate} key={index} />
          })
        }
        { isActive?
          'Release to drop' : 'drag a card here'
        }
      </div>
    );
  }
}

Card.jsx=

import React, { PropTypes } from 'react';
import { DragSource } from 'react-dnd';
import ItemTypes from './ItemTypes';


const cardSource = {
  beginDrag(props) {
    return {
      candidate_id: props.candidate.id,
    };
  },

  endDrag(props, monitor) {
    const item = monitor.getItem();
    const dropResult = monitor.getDropResult();

    if (dropResult) {
      console.log(`You dropped ${item.candidate_id} vers ${dropResult.status} !`);
      $.post(`/update_status/${item.candidate_id}/${dropResult.status}`);
    }
  },
};

@DragSource(ItemTypes.CARD, cardSource, (connect, monitor) => ({
  connectDragSource: connect.dragSource(),
  isDragging: monitor.isDragging(),
}))

export default class Card extends React.Component {

  constructor(props, _railsContext) {
    super(props);
  }

  render() {
    const { isDragging, connectDragSource } = this.props;
    const { name } = this.props;
    const opacity = isDragging ? 0 : 1;

    var candidate = this.props.candidate;

    return (
      connectDragSource(
        <div className="card" key={candidate.id} style={{opacity}}>
          <div className="content">
            <div className="header">{`${candidate.first_name} ${candidate.last_name}`}</div>
            <div className="description">
              {candidate.job_title}
            </div>
            <span className="right floated">
              <i className="heart outline like icon"></i>
              {candidate.average_rate}
            </span>
          </div>
        </div>
      )
    );
  }
}

为了更好地理解,这里是我的功能和他的错误的 gif 图像: 有错误的功能

这是我的 github repo 与所有代码

4

5 回答 5

3

试着制作HTML5Backend一个单例的上下文。您可以在以下位置查看代码:

https://github.com/react-dnd/react-dnd/issues/186

https://github.com/react-dnd/react-dnd/issues/708

于 2017-09-14T07:05:32.290 回答
2

我遇到了类似的问题,并通过移动找到了解决方法

<DragDropContextProvider></DragDropContextProvider>

到最顶层的反应组件。

之前的结构:

  App
    Component1
      Component2
        ...
        ComponentX with render(<DragDropContextProvider>...<DragDropContextProvider>)

之后的结构:

  App with render(<DragDropContextProvider>...<DragDropContextProvider>)
    Component1 
      Component2
        ...
        ComponentX 

假设 App 只加载一次,并且没有复制 HTML5 后端。

于 2018-12-02T16:17:14.953 回答
0

您可以创建一个新文件并DragDropContext在需要的位置导入:

import HTML5 from 'react-dnd-html5-backend';
import {DragDropContext} from 'react-dnd';
export default DragDropContext(HTML5);

来源:https ://github.com/react-dnd/react-dnd/issues/708#issuecomment-309259695

或使用钩子:

import { DndProvider, createDndContext } from "react-dnd";
import HTML5Backend from "react-dnd-html5-backend";
import React, { useRef } from "react";

const RNDContext = createDndContext(HTML5Backend);

function useDNDProviderElement(props) {
  const manager = useRef(RNDContext);

  if (!props.children) return null;

  return <DndProvider manager={manager.current.dragDropManager}>{props.children}</DndProvider>;
}

export default function DragAndDrop(props) {
  const DNDElement = useDNDProviderElement(props);
  return <React.Fragment>{DNDElement}</React.Fragment>;
}

利用:

import DragAndDrop from "../some/path/DragAndDrop";

export default function MyComp(props){
   return <DragAndDrop>....<DragAndDrop/>
}

来源:https ://github.com/react-dnd/react-dnd/issues/186#issuecomment-561631584

如果您使用BrowserRouter然后DragDropContextProvider移出BrowserRouter

来源:https ://github.com/react-dnd/react-dnd/issues/186#issuecomment-453990887

于 2020-11-05T13:08:54.197 回答
0

将 DragDropContextProvider移出 BrowserRouter

import { BrowserRouter as Router } from 'react-router-dom';
import { DndProvider } from 'react-dnd';

<DndProvider>
  <Router>
    <App />
  </Router>
</DndProvider>
于 2022-01-25T07:27:21.173 回答
0

DndProvider有一个options道具,您可以在其中设置rootElementDnD 到该指定上下文的边界,不幸的是它没有很好地记录。这种方法解决了我所有的问题,因为我有其他使用 DnD 的组件,它们超出了我的范围,我无法制作HTML5Backend单例。我尝试了这种方法"react-dnd": "^14.0.2"

const myFirstId = 'first-DnD-Containier';
const mySecondId = 'second-DnD-Containier';

export const DndWrapper = React.memo((props) => {
  const [context, setContext] = useState(null);

  useEffect(() => {
    setContext(document.getElementById(props.id))
  },[props.id])

  return context ? (
      <DndProvider backend={HTML5Backend} options={{ rootElement: context}}>
          {props.children}
      </DndProvider>
  ) : null;
});

export default function App() {
  return (
    <div>
      <div id={myFirstId}>
        <DndWrapper id={myFirstId}>
               <MyOtherComponents /> 
        </DndWrapper>
      </div>
      <div id={mySecondId}>
          <DndWrapper id={mySecondId}>
            <MyOtherComponents />
          </DndWrapper>
      </div>
    </div>
  );
}

PS 确保在您调用document.getElementByIdDOM 时存在 ids。

于 2021-11-24T20:36:20.113 回答