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
Just try to make the context of HTML5Backend
a singleton. You can check the codes at: