Rails5, action Cable, React and Rails, React DnDλ‘ POCλ₯Ό λ§λ€λ €κ³ ν©λλ€.
λͺ©μ μ trelloμ κ°μ μ±μ λ§λλ κ²μ΄μ§λ§ λͺ¨μ§ νλ‘μΈμ€λ₯Ό μν κ²μ λλ€.
λ΄ μ λ©΄μ ReactJSμ μμ΅λλ€.
3κ°μ κ΅¬μ± μμκ° μμ΅λλ€. λ¨Όμ 컨ν μ΄λλ "Candidates"λ₯Ό νΈμΆνκ³ μ΄ κ΅¬μ± μμλ "Card" κ΅¬μ± μμλ₯Ό νΈμΆνλ 2κ°μ "CardBoard" κ΅¬μ± μμλ₯Ό νΈμΆν©λλ€.
λλκ·Έ κ°λ₯ν μΉ΄λ λ° λκΈ° κ°λ₯ν CardBoardμ λν΄ μ¬μ©μκ° DnD λΌμ΄λΈλ¬λ¦¬μ λ°μν©λλ€. μΉ΄λ보λμ μΉ΄λλ₯Ό λ¨μ΄λ¨λ¦΄ λ λ΄ μνλ₯Ό μ λ°μ΄νΈνκΈ° μν΄ ν¬μ€νΈ μ½κ³Ό μΉ μμΌ(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>
);
}
}
μΉ΄λ보λ.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>
);
}
}
μΉ΄λ.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κ° μμ΅λλ€.
@antoineBernard μ΅κ·Όμ κ°μ μ€λ₯λ₯Ό 극볡νλ €κ³ ν루 μ’ μΌ λ³΄λμ΅λλ€. νμ€νμ§ μμ§λ§ κ·νμ λΉμ·ν λ¬Έμ μΌ μ μμ΅λλ€. λ€μ 2κ°μ§ μ€ νλλ₯Ό μλν΄ λ³΄μΈμ.
<DragDropContextProvider>
λ₯Ό Candidates.jsx
μ μμ ꡬμ±μμλ‘ μ΄λν΄ λ³΄μμμ€.DragDropContext
μ λ¨μΌ μΈμ€ν΄μ€λ§ μ± μ 체μμ μ΄κΈ°νλλλ‘ ν©λλ€. @gcorne μ μ μ μ μ±κ³΅μ μΌλ‘ μ¬μ©νμ΅λλ€.λΉμ μκ²λ ν¨κ³Όκ° μκΈ°λ₯Ό λ°λλλ€!
μ΄κ²μ λλ₯Ό μν΄ μΌνμ΅λλ€ : https://github.com/react-dnd/react-dnd/issues/186#issuecomment -282789420
μ΄ λ¬Έμ λ μ΅κ·Ό νλμ΄ μμκΈ° λλ¬Έμ μλμΌλ‘ μ€λλ κ²μΌλ‘ νμλμμ΅λλ€. λ μ΄μ νλμ΄ μμΌλ©΄ νμλ©λλ€. κ·νμ κΈ°μ¬μ κ°μ¬λ립λλ€.
κ°μ₯ μ μ©ν λκΈ
@antoineBernard μ΅κ·Όμ κ°μ μ€λ₯λ₯Ό 극볡νλ €κ³ ν루 μ’ μΌ λ³΄λμ΅λλ€. νμ€νμ§ μμ§λ§ κ·νμ λΉμ·ν λ¬Έμ μΌ μ μμ΅λλ€. λ€μ 2κ°μ§ μ€ νλλ₯Ό μλν΄ λ³΄μΈμ.
<DragDropContextProvider>
λ₯ΌCandidates.jsx
μ μμ ꡬμ±μμλ‘ μ΄λν΄ λ³΄μμμ€.DragDropContext
μ λ¨μΌ μΈμ€ν΄μ€λ§ μ± μ 체μμ μ΄κΈ°νλλλ‘ ν©λλ€. @gcorne μ μ μ μ μ±κ³΅μ μΌλ‘ μ¬μ©νμ΅λλ€.λΉμ μκ²λ ν¨κ³Όκ° μκΈ°λ₯Ό λ°λλλ€!