我想做一个可拖动(即可以通过鼠标重新定位)的 React 组件,这似乎必然涉及全局状态和分散的事件处理程序。我可以用肮脏的方式做到这一点,在我的 JS 文件中使用一个全局变量,甚至可以将它包装在一个漂亮的闭包接口(interface)中,但我想知道是否有一种方法可以更好地与 React 结合。
此外,由于我以前从未在原始 JavaScript 中做过这件事,所以我想看看专家是如何做的,以确保我已经处理了所有极端情况,尤其是与 React 相关的情况。
谢谢。
最佳答案
我可能应该把它写成一篇博文,但这里有一个非常可靠的例子。
评论应该很好地解释了事情,但如果您有任何问题,请告诉我。
这里是 fiddle :https://jsfiddle.net/Af9Jt/2/
var Draggable = React.createClass({
getDefaultProps: function () {
return {
// allow the initial position to be passed in as a prop
initialPos: {x: 0, y: 0}
}
},
getInitialState: function () {
return {
pos: this.props.initialPos,
dragging: false,
rel: null // position relative to the cursor
}
},
// we could get away with not having this (and just having the listeners on
// our div), but then the experience would be possibly be janky. If there's
// anything w/ a higher z-index that gets in the way, then you're toast,
// etc.
componentDidUpdate: function (props, state) {
if (this.state.dragging && !state.dragging) {
document.addEventListener('mousemove', this.onMouseMove)
document.addEventListener('mouseup', this.onMouseUp)
} else if (!this.state.dragging && state.dragging) {
document.removeEventListener('mousemove', this.onMouseMove)
document.removeEventListener('mouseup', this.onMouseUp)
}
},
// calculate relative position of the mouse and set dragging=true
onMouseDown: function (e) {
// only left mouse button
if (e.button !== 0) return
var pos = $(this.getDOMNode()).offset()
this.setState({
dragging: true,
rel: {
x: e.pageX - pos.left,
y: e.pageY - pos.top
}
})
e.stopPropagation()
e.preventDefault()
},
onMouseUp: function (e) {
this.setState({dragging: false})
e.stopPropagation()
e.preventDefault()
},
onMouseMove: function (e) {
if (!this.state.dragging) return
this.setState({
pos: {
x: e.pageX - this.state.rel.x,
y: e.pageY - this.state.rel.y
}
})
e.stopPropagation()
e.preventDefault()
},
render: function () {
// transferPropsTo will merge style & other props passed into our
// component to also be on the child DIV.
return this.transferPropsTo(React.DOM.div({
onMouseDown: this.onMouseDown,
style: {
left: this.state.pos.x + 'px',
top: this.state.pos.y + 'px'
}
}, this.props.children))
}
})
关于国有制等的思考
“谁应该拥有什么国家”是一个从一开始就需要回答的重要问题。对于“可拖动”组件,我可以看到几种不同的场景。
场景一
父级应拥有可拖动对象的当前位置。在这种情况下,可拖动对象仍将拥有自己的“我被拖动了吗”状态,但会在 mousemove
事件发生时调用 this.props.onChange(x, y)
发生。
场景2
父级只需要拥有“非移动位置”,因此可拖动对象将拥有它的“拖动位置”,但 onmouseup
会调用 this.props.onChange(x, y)
并将最终决定权交给父级。如果父级不喜欢可拖动对象的最终位置,它会拒绝状态更新,并且可拖动对象会“弹回到”其初始位置。
混合还是组件?
@ssorallen 指出,因为 draggable
与其说是一个事物本身,不如说是一个属性,它可能更适合作为 mixin。我对 mixin 的经验有限,所以我还没有看到它们在复杂情况下如何提供帮助或阻碍。这很可能是最佳选择。
关于javascript - 使 React 组件/div 可拖动的推荐方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20926551/