English 中文(简体)
React DnD - "Cannot have two HTML5 backends at the same time."
原标题:

I am trying to make a POC with Rails5, action Cable, React and Rails and React DnD.

The purpose is to make an app like trello but for an recruitment process.

My front is in ReactJS.

I have 3 components, first, the container call "Candidates", this component call 2 "CardBoard" components that call "Card" component.

I user react DnD library for draggable card and droppable CardBoard. when i drop card on cardboard, i use a post call and a websocket(action cable from rails5) for update my state. I don t understand why i have this message after the post call :

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)

Candidate.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>
      )
    );
  }
}

for better comprehension, here a gif of my feature and his bug : feature with bug

here my github repo with all code

问题回答

Just try to make the context of HTML5Backend a singleton. You can check the codes at:

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

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

I faced with similar issue and found workaround by moving

<DragDropContextProvider></DragDropContextProvider>

to the topmost react component.

Structure before:

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

Structure after:

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

Supposing App is loaded only once and HTML5 backend is not replicated.

DndProvider has a options prop in where you can set rootElement which bounds DnD to that specified context, and unfortunately it isn t documented well. This approach solved all my issues, as I had other component which was using DnD and they were out of my boundary and I wasn t able to make HTML5Backend Singleton. I tried this approach with "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>
  );
}

P.S. Make sure by the time you call document.getElementById the DOM exist with ids.

You can either create a new file and import DragDropContext where you need it:

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

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

Or using Hooks:

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

Use:

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

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

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

If you use BrowserRouter then move the DragDropContextProvider out of BrowserRouter:

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

Move the DragDropContextProvider out of BrowserRouter.

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

<DndProvider>
  <Router>
    <App />
  </Router>
</DndProvider>

Spend a day half but was worthy to get a solution ?

declare const window: Window &
    typeof globalThis & {
        __isReactDndBackendSetUp: boolean
    }
class ConnectedFabricsView extends React.PureComponent <{},{} > {
    componentDidMount(): void {
        if (window.__isReactDndBackendSetUp) {
            window.__isReactDndBackendSetUp = false
        }
    }
}




相关问题
Where to get the "latest" ASP .NET Preview DLL and Script

I have been trying to download the ASP .NET AJAX Futures but I can only see old versions of the DLL via sample tutorials from other websites. I need the DLL to use the Drag and Drop feature according ...

To MVVM or not to MVVM that is the question [closed]

I am rewriting my windows forms based application and I am going to use WPF. The application relies heavily on drag and drop techniques, in a very graphically environment. The uses "Design" reports ...

Make table rows sortable with YUI

Currently I have a table similar to below: <table id="tbl"> <thead> <tr><th>Example Title</th><th>Title2</th></tr> </thead> &...

Drag & drop with .ui files

I m having big troubles with drag & drop. I ve created a new Qt Designer Form Class in which I have one QListWidget and one QWidget. Now I want to enable dragging & dropping between these two ...

Java中校正

当我把档案从桌面上拖到浏览器时,我把浏览器放在重点(Safari或PireFox,现在),我知道一切工作权(拖拉,有回信等)。 我也能够......

热门标签