I have taken an example of dragging certain div in react js from here http://jsfiddle.net/Af9Jt/2/
Now it is in createClass and I need to convert it into class Draggable extends React.Component
in order to export it into another component. Here is code
APP.JS
import React from 'react'; import './App.css'; import Draggable from './Draggable.js'; function App() { return ( <React.Fragment> <Draggable /> </React.Fragment> ); } export default App;
Draggable.js
import React from 'react'; export class Draggable extends React.Component{ constructor(props) { super(props); this.state = { pos: {x: 0, y: 0}, dragging: false, rel: null }; this.onMouseMove = this.onMouseMove.bind(this); this.onMouseDown = this.onMouseDown.bind(this); this.onMouseUp = this.onMouseUp.bind(this); } // 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(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 to the mouse and set dragging=true onMouseDown(e) { console.log("1") console.log(this.state); if (e.button !== 0) return this.setState({ dragging: true, rel: { x: e.pageX - e.nativeEvent.offsetX, y: e.pageY - e.nativeEvent.offsetY } }) e.stopPropagation() e.preventDefault() } onMouseUp(e) { this.setState({dragging: false}) e.stopPropagation() e.preventDefault() } onMouseMove(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() { return( <div style={{position: "absolute", left: "175px", top: "65px", border: "2px solid rgb(170, 170, 85)", padding: "10px"}} className="my-draggable" data-reactid=".r[2zxee]" id="messi" onMouseDown={this.onMouseDown} onMouseUp={this.onMouseUp} onMouseDown={this.onMouseDown} initialPos = {{x:0,y:0}} > Drag Me! See how children are passed through to the div! </div> ) } } export default Draggable;
Everything runs fine in this code the box is shown but I cannot drag the div, I couldn’t figure out what issue is this. How Can I Solve this?
Here is my sample code in jsfiddle
https://jsfiddle.net/6vdurk79/3/
Advertisement
Answer
There were a few things I noticed when converting this into a React.Component:
- You never used the
this.state.pos
when rendering, so even if the position changed in the variables, it wouldn’t move the div. Thestyle
attribute of the<div>
is just hard-coded with{ left: "175px", top: "65px" }
- You didn’t properly get the position of the mouse in your
this.onMouseDown
function, which caused it to forced every movement to be at the corner. - You never bound
this.onMouseMove
to anything. Uncommenting the big chunk of commented out code fixed this. - The
initialPos
attribute you place inside the<div>
does absolutely nothing. I converted that into a prop in the constructor.
Here’s the updated JSFiddle link: https://jsfiddle.net/ogy4xd1c/3/
And I’ll embed it here on StackOverflow in a snippet.
class Draggable extends React.Component { constructor(props) { super(props); this.state = { pos: props.initialPos || { x: 0, y: 0 }, dragging: false, rel: null } this.onMouseMove = this.onMouseMove.bind(this); this.onMouseDown = this.onMouseDown.bind(this); this.onMouseUp = this.onMouseUp.bind(this); } // calculate relative position to the mouse and set dragging=true onMouseDown(e) { if (e.button !== 0) return const de = document.documentElement; const box = ReactDOM.findDOMNode(this).getBoundingClientRect(); const top = box.top + window.pageYOffset - de.clientTop; const left = box.left + window.pageXOffset - de.clientLeft; this.setState({ dragging: true, rel: { x: e.pageX - left, y: e.pageY - top, } }) e.stopPropagation() e.preventDefault() } onMouseUp(e) { this.setState({ dragging: false }) e.stopPropagation() e.preventDefault() } onMouseMove(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() } componentDidUpdate(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) } } render() { return ( <div style={{ position: "absolute", left: this.state.pos.x, top: this.state.pos.y, border: "2px solid rgb(170, 170, 85)", padding: "10px" }} className="my-draggable" data-reactid=".r[2zxee]" id="messi" onMouseDown={this.onMouseDown} className="my-draggable" > Drag Me! See how children are passed through to the div! </div> ) } } ReactDOM.render(<Draggable initialPos={{ x: 50, y: 20 }} />, document.querySelector("#root"));
.my-draggable { cursor: pointer; width: 200px; height: 200px; background-color: #cca; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script> <div id="root"></div>
If you want to pass in children, you can do that too with this modified version: https://jsfiddle.net/hceLjz90/
class Draggable extends React.Component { constructor(props) { super(props); this.state = { pos: props.initialPos || { x: 0, y: 0 }, dragging: false, rel: null } this.onMouseMove = this.onMouseMove.bind(this); this.onMouseDown = this.onMouseDown.bind(this); this.onMouseUp = this.onMouseUp.bind(this); } // calculate relative position to the mouse and set dragging=true onMouseDown(e) { if (e.button !== 0) return const de = document.documentElement; const box = ReactDOM.findDOMNode(this).getBoundingClientRect(); const top = box.top + window.pageYOffset - de.clientTop; const left = box.left + window.pageXOffset - de.clientLeft; this.setState({ dragging: true, rel: { x: e.pageX - left, y: e.pageY - top, } }) e.stopPropagation() e.preventDefault() } onMouseUp(e) { this.setState({ dragging: false }) e.stopPropagation() e.preventDefault() } onMouseMove(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() } componentDidUpdate(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) } } render() { return ( <div style={{ position: "absolute", left: this.state.pos.x, top: this.state.pos.y, border: "2px solid rgb(170, 170, 85)", padding: "10px" }} className="my-draggable" data-reactid=".r[2zxee]" id="messi" onMouseDown={this.onMouseDown} className="my-draggable" > {this.props.children} </div> ) } } ReactDOM.render(<Draggable initialPos={{ x: 50, y: 20 }}> <h1>This is a child element</h1> <p>This is also a child element</p> </Draggable>, document.querySelector("#root"))
.my-draggable { cursor: pointer; width: 200px; height: 200px; background-color: #cca; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script> <div id="root"></div>