React - Render happening before state is changed - javascript

Very confused about this one. From the code, If you move the green square you will see the printed values.
I expected all of the code to run before the state is changed and the rerender however from this code example i can see the rerender happens as soon as the function to change state is called.
import React, { useState, useEffect, useRef } from "react";
import styled, { ThemeProvider, css } from "styled-components";
export default function App() {
const pos = useRef({ x: 0, y: 0 });
const offset = useRef({ x: 0, y: 0 });
const myDiv = useRef(null);
const [rerender, setRerender] = useState(0);
console.log("render");
useEffect(() => {
window.addEventListener("mousemove", doSomething);
window.addEventListener("mousedown", doSomethingAgain);
return () => {
window.removeEventListener("mousemove", doSomething);
window.removeEventListener("mousedown", doSomethingAgain);
};
}, [rerender]);
console.log("global", rerender);
function doSomething(e) {
if (e.buttons == 1) {
const newPos = {
x: e.pageX,
y: e.pageY
};
pos.current = newPos;
console.log("first", rerender);
setRerender(rerender + 1);
console.log("second", rerender);
}
}
function doSomethingAgain(e) {
const offsetX = e.pageX - myDiv.current.offsetLeft;
const offsetY = e.pageY - myDiv.current.offsetTop;
offset.current = { x: offsetX, y: offsetY };
}
return (
<div className="App">
<DIV ref={myDiv} off={offset.current} pos={pos.current}></DIV>
</div>
);
}
const DIV = styled.div`
display: inline-block;
width: 100px;
height: 100px;
background-color: green;
position: absolute;
top: ${({ pos, off }) => pos.y - off.y}px;
left: ${({ pos, off }) => pos.x - off.x}px;
`;

Related

Canvas scale with dpr redraw issues

When I add scale and dpr to my drawing app's canvas to improve the look of objects on the canvas, it produces issues when resizing the window. The scale seems to be off and possibly flickering between 2 different scales as you change window size quickly? I can't seem to figure out the cause of this. As soon as I remove scale and dpr, then everything works fine. Here's my code and a codesandbox with it. https://codesandbox.io/s/holy-wave-t9y2g0?file=/src/App.js
import React, { useEffect, useRef, useState } from "react";
import getStroke from "perfect-freehand";
function getSvgPathFromStroke(stroke) {
if (!stroke.length) return "";
const d = stroke.reduce(
(acc, [x0, y0], i, arr) => {
const [x1, y1] = arr[(i + 1) % arr.length];
acc.push(x0, y0, (x0 + x1) / 2, (y0 + y1) / 2);
return acc;
},
["M", ...stroke[0], "Q"]
);
d.push("Z");
return d.join(" ");
}
export default function App() {
const [elements, setElements] = useState([]);
const wrapperRef = useRef(null);
const canvasRef = useRef(null);
const [isDrawing, setIsDrawing] = useState(false);
const [dimensions, setDimensions] = useState({
height: 0,
width: 0
});
const elementsRef = useRef(elements);
const dpr = window.devicePixelRatio || 1;
// in place of original `setElements`
const setElementsRef = (x) => {
elementsRef.current = x; // keep updated
setElements(x);
};
const setNewElements = ({ type, points, ...rest }) => {
setElementsRef([...elements, { type, points, ...rest }]);
};
useEffect(() => {
document.body.style.overscrollBehavior = "contain";
window.addEventListener("resize", () => {
resizeCanvas();
});
}, []);
useEffect(() => {
if (wrapperRef.current && canvasRef.current) {
setupCanvas(true);
}
}, [wrapperRef.current, canvasRef.current]);
useEffect(() => {
drawCanvas();
}, [elements]);
const drawCanvas = () => {
const context = canvasRef.current.getContext("2d");
context.clearRect(0, 0, dimensions.width * dpr, dimensions.height * dpr);
elementsRef.current.forEach((element) => {
const stroke = getStroke(element.points);
const pathData = getSvgPathFromStroke(stroke);
const myPath = new Path2D(pathData);
context.fill(myPath);
});
};
const setupCanvas = () => {
const newHeight = wrapperRef.current.clientHeight ?? 0;
const newWidth = wrapperRef.current.clientWidth ?? 0;
setDimensions({
height: newHeight,
width: newWidth
});
const context = canvasRef?.current?.getContext("2d");
context.scale(dpr, dpr);
return new Promise((fulfill) => fulfill(true));
};
const resizeCanvas = () => {
if (canvasRef.current) {
setupCanvas().then(() => drawCanvas());
}
};
function handleMouseDown(e) {
setIsDrawing(true);
if (canvasRef.current) {
const bounds = canvasRef?.current?.getBoundingClientRect();
const x = e.pageX - bounds.left - window.scrollX;
const y = e.pageY - bounds.top - window.scrollY;
setNewElements({ type: "pen", points: [[x, y]] });
}
}
function handleMouseMove(e) {
if (canvasRef.current && isDrawing) {
const bounds = canvasRef.current.getBoundingClientRect();
const x = e.pageX - bounds.left - window.scrollX;
const y = e.pageY - bounds.top - window.scrollY;
const arr = [...elements];
arr[arr.length - 1].points.push([x, y]);
setElementsRef(arr);
}
}
function handleMouseUp(e) {
setIsDrawing(false);
}
return (
<div
id="wrapper"
style={{ height: "100vh", width: "100vw", overflow: "hidden" }}
ref={wrapperRef}
>
<canvas
id="canvas"
ref={canvasRef}
style={{ height: "100%", width: "100%" }}
width={dimensions.width * dpr}
height={dimensions.height * dpr}
onMouseDown={handleMouseDown}
onMouseMove={handleMouseMove}
onMouseUp={handleMouseUp}
/>
</div>
);
}

How Can I convert React.createclass to Class Component?

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/
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. The style 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>

Drag Background in React

I 'm new in React and i'm tryind to make draggable background, but no matter how hard I try, nothing happens. I found some code on jQuery, but there many advices that it's bad practice use jQuery in React.
Maybe i make something wrong.
Thanks in advance
Here's my React code
import React from "react";
import "../styles/board.css";
class Board extends React.Component {
constructor(props) {
super(props);
this.state = { mouseCliked: 0, startX: 0, startY: 0 };
}
mouseDown(e) {
this.setState({ mouseCliked: 1, startX: e.clientX, startY: e.clientY });
}
mouseUp(e) {
this.setState({ mouseCliked: 0, startX: e.clientX, startY: e.clientY });
}
mouseMove = (e) => {
let newPosY = e.clientY - this.stateY;
let newPosX = e.clientX - this.stateX;
if (this.state.mouseClicked) {
e.target.style.backgroundPositionX += newPosX;
e.target.style.backgroundPositionY += newPosY;
}
};
render() {
return (
<div
onMouseMove={this.mouseMove.bind(this)}
onMouseUp={this.mouseUp.bind(this)}
onMouseDown={this.mouseDown.bind(this)}
className="background-image"
>
</div>
);
}
}
export default Board;
CSS:
width:300px;
height: 300px;
background-size: 1000px;
background-position-x: 0;
background-position-y: 0;
background-image: url('https://images.unsplash.com/photo-1452723312111-3a7d0db0e024?crop=entropy&dpr=2&fit=crop&fm=jpg&h=750&ixjsv=2.1.0&ixlib=rb-0.3.5&q=50&w=1450.jpg');
}
I have encountered this problem and also checked jQuery - drag div css background
Finally I came up with this solution and seemed working fine.
const imageStyleInitialValue = {
backgroundImage: "",
backgroundPosition: "0 0",
backgroundSize: "0 0",
height: 0,
width: 0,
};
const [startPoint, setStartPoint] = useState({x: 0, y: 0});
const [dragging, setDragging] = useState(false);
const [imageStartPos, setImageStartPos] = useState([0, 0]);
const [imageStyles, setImageStyles] = useState<ImageStyles>(imageStyleInitialValue);
// add onMouseMove={handleDragImage} to the image component
const handleDragImage = (e) => {
if (dragging) {
const deltaX = e.clientX - startPoint.x;
const deltaY = e.clientY - startPoint.y;
setImageStyles({...imageStyles,
backgroundPosition:
`${imageStartPos[0] + deltaX} ${imageStartPos[1] + deltaY}`
})
}
};
// add onMouseDown={handleStartDragImage} to the image component
const handleStartDragImage = (e) => {
setDragging(true);
const backgroundPosArray = imageStyles.backgroundPosition.split(" ").map(value => Number(value));
setImageStartPos(backgroundPosArray);
setStartPoint({x: e.clientX, y: e.clientY});
}
// add onMouseUp={handleEndDragImage} to the top component because you want
// to set Dragging to false when the dragging ends outside of the image
const handleEndDragImage = (e) => {
setDragging(false)
};

Property 'current' does not exist on type '[boolean, Dispatch<SetStateAction<boolean>>]'.ts(2339)

I am adding cursor animations to a React/Typescript project and in researching came across a CodePen (Animated Cursor
React Component) that works perfectly well.
However, when converting to a Typescript file I come across the error Property 'current' does not exist on type '[boolean, Dispatch<SetStateAction<boolean>>]'.ts(2339) on cursorVisible.current in
const onMouseEnter = () => {
cursorVisible.current = true;
toggleCursorVisibility();
};
Property cursorVisible is from const cursorVisible = useState(false);
What does Typescript need me to do so current works in Typescript? Reading the React Hooks docs, I could not see reference to current on useState and interestingly this works as a js file, only not in ts.
In the past I have used current with ref but never across useState hook.
Full file is
import React, { useEffect, useRef, useState } from 'react';
import MobileDetect from './MobileDetect';
interface CursorProps {
color: string;
outlineAlpha: number;
dotSize: number;
outlineSize: number;
outlineScale: number;
dotScale: number;
}
function AnimatedCursor({
color = '220, 90, 90',
outlineAlpha = 0.3,
dotSize = 8,
outlineSize = 8,
outlineScale = 5,
dotScale = 0.7,
}: CursorProps) {
// Bail if Mobile
if (typeof navigator !== 'undefined' && MobileDetect!.anyMobile())
return <></>;
const cursorOutline = useRef();
const cursorDot = useRef();
const requestRef = useRef();
const previousTimeRef = useRef();
const [width, setWidth] = useState(window.innerWidth);
const [height, setHeight] = useState(window.innerHeight);
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
const cursorVisible = useState(false);
const cursorEnlarged = useState(false);
const styles = {
cursors: {
zIndex: 999,
pointerEvents: 'none',
position: 'absolute',
top: '50%',
left: '50%',
borderRadius: '50%',
opacity: 0,
transform: 'translate(-50%, -50%)',
transition: 'opacity 0.15s ease-in-out, transform 0.15s ease-in-out',
},
cursorDot: {
width: dotSize,
height: dotSize,
backgroundColor: `rgba(${color}, 1)`,
},
cursorOutline: {
width: outlineSize,
height: outlineSize,
backgroundColor: `rgba(${color}, ${outlineAlpha})`,
},
};
// Hide default cursor
document.body.style.cursor = 'none';
// Mouse Events
const onMouseMove = (event: { pageX: number; pageY: number }) => {
const { pageX: x, pageY: y } = event;
setMousePosition({ x, y });
positionDot(event);
};
const onMouseEnter = () => {
cursorVisible.current = true;
toggleCursorVisibility();
};
const onMouseLeave = () => {
cursorVisible.current = false;
toggleCursorVisibility();
};
const onMouseDown = () => {
cursorEnlarged.current = true;
toggleCursorSize();
};
const onMouseUp = () => {
cursorEnlarged.current = false;
toggleCursorSize();
};
// Set window hxw
const onResize = () => {
setWidth(window.innerWidth);
setHeight(window.innerHeight);
};
/**
* Hooks
*/
useEffect(() => {
// Bail if mobile
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseenter', onMouseEnter);
document.addEventListener('mouseleave', onMouseLeave);
document.addEventListener('mousedown', onMouseDown);
document.addEventListener('mouseup', onMouseUp);
window.addEventListener('resize', onResize);
requestRef.current = requestAnimationFrame(animateDotOutline);
handleLinkEvents();
return () => {
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseenter', onMouseEnter);
document.removeEventListener('mouseleave', onMouseLeave);
document.removeEventListener('mousedown', onMouseDown);
document.removeEventListener('mouseup', onMouseUp);
window.removeEventListener('resize', onResize);
cancelAnimationFrame(requestRef.current);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
let { x, y } = mousePosition;
const winDimensions = { width, height };
let endX = winDimensions.width / 2;
let endY = winDimensions.height / 2;
/**
* Toggle Cursor Visiblity
*/
function toggleCursorVisibility() {
if (cursorVisible.current) {
cursorDot.current.style.opacity = 1;
cursorOutline.current.style.opacity = 1;
} else {
cursorDot.current.style.opacity = 0;
cursorOutline.current.style.opacity = 0;
}
}
/**
* Position Dot (cursor)
* #param {event}
*/
function positionDot(e: { pageX: number; pageY: number }) {
cursorVisible.current = true;
toggleCursorVisibility();
// Position the dot
endX = e.pageX;
endY = e.pageY;
cursorDot.current.style.top = `${endY}px`;
cursorDot.current.style.left = `${endX}px`;
}
/**
* Toggle Cursors Size/Scale
*/
function toggleCursorSize() {
if (cursorEnlarged.current) {
cursorDot.current.style.transform = `translate(-50%, -50%) scale(${dotScale})`;
cursorOutline.current.style.transform = `translate(-50%, -50%) scale(${outlineScale})`;
} else {
cursorDot.current.style.transform = 'translate(-50%, -50%) scale(1)';
cursorOutline.current.style.transform = 'translate(-50%, -50%) scale(1)';
}
}
/**
* Handle Links Events
* Applies mouseover/out hooks on all links
* to trigger cursor animation
*/
function handleLinkEvents() {
document.querySelectorAll('a').forEach((el) => {
el.addEventListener('mouseover', () => {
cursorEnlarged.current = true;
toggleCursorSize();
});
el.addEventListener('mouseout', () => {
cursorEnlarged.current = false;
toggleCursorSize();
});
});
}
/**
* Animate Dot Outline
* Aniamtes cursor outline with trailing effect.
* #param {number} time
*/
const animateDotOutline = (time: undefined) => {
if (previousTimeRef.current !== undefined) {
x += (endX - x) / 8;
y += (endY - y) / 8;
cursorOutline.current.style.top = `${y}px`;
cursorOutline.current.style.left = `${x}px`;
}
previousTimeRef.current = time;
requestRef.current = requestAnimationFrame(animateDotOutline);
};
return (
<>
<div
ref={cursorOutline}
id="cursor-outline"
style={{ ...styles.cursors, ...styles.cursorOutline }}
/>
<div
ref={cursorDot}
id="cursor-inner"
style={{ ...styles.cursors, ...styles.cursorDot }}
/>
</>
);
}
export default AnimatedCursor;
Posting here in case someone else ran into this issue:
My problem was that i was using curly braces instead brackets when destructuring. Brain fart moment, but basically I was attempting to use the Object destructure notation on the array that's returned from useState.
const { someState, setSomeState } = useState(false);
should be
const [ someState, setSomeState ] = useState(false);
You'll have to ask the author of the code you're using. useState returns an array with the current value and a setter function to change the value. Normally you would use it like this:
let [cursorVisible, setCursorVisible] = useState(false);
// instead of cursorVisible.current = true
setCursorVisible(true);
There's no 'current' property on the array, unless maybe it is set by other code which would be bad form I think.

Using initialPos from Reactjs state

I am building a React component with fixed width and resizable height. The problem is I am getting event.clientY and this.state.initialPos with unpredictable values. Here is fiddle
And here is code of React component
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import '../styles/cal-event-styles.css';
class CalEvent extends Component {
constructor(props) {
super(props);
this.state = {
isDragging: false,
height: 40,
text: this.props.text,
color: this.props.color
}
}
componentDidMount() {
ReactDOM.findDOMNode(this).addEventListener('mousemove', this.resizePanel);
ReactDOM.findDOMNode(this).addEventListener('mouseup', this.stopResize);
ReactDOM.findDOMNode(this).addEventListener('mouseleave', this.stopResize);
}
resizePanel = (event) => {
if (this.state.isDragging) {
//I tried to use event.clientY - this.state.initialPos but it doesn't work
let delta = event.clientY + this.state.height;
console.log("event.clentY " + event.clientY);
console.log("this.state.initialPos " + this.state.initialPos);
this.setState({height: delta});
}
}
stopResize = () => {
if (this.state.isDragging) {
this.setState({
isDragging: false,
});
}
// height: this.getStep(this.state.height)
}
getStep = (height) => {
return Math.floor(height / 50) * 50;
}
startResize = (event) => {
this.setState({
isDragging: true,
initialPos: event.clientX
});
}
formatText = () => {
const { text, height } = this.state;
return text.length > 10 && height <= 100 ? text.substring(0, 14) + "..." : text;
}
render(){
const {color, text, height, isDragging, initialPos } = this.state;
console.log("this.state.isDragging: " + isDragging);
if (isDragging) {
console.log("this.state.height: " + height);
console.log("this.state.initialPos: " + initialPos);
}
return(
<div className="cal-event" onMouseUp={() => this.stopResize()} style={{height: `${height}px`, background: color}}>
<div className="cal-event-tile"><p>{this.formatText()}</p></div>
<div className="resizer-height" onMouseDown={e => this.startResize(e)}></div>
</div>
)
}
}
export default CalEvent;
I think that in this line:
let delta = event.clientY + this.state.height
delta shouldn't be the sum of those values, because you resize on the Y axis and exactly the Y (in this case) should determine the new height:
I tried this in the resizePanel method and it seem to work:
this.setState({ height: event.clientY })
The problem with this approach is that, in this case, the top position of the component is almost 0, and that is why it work quite seemlessly.
If you need it to work no matter where the component is placed on the page, you have to compute delta as the difference between the top position and the event.clientY.
To do it, I would save the absolute top position of the component in the componentDidMount lifecycle method:
componentDidMount() {
const currentNode = ReactDOM.findDOMNode(this)
currentNode.addEventListener('mousemove', this.resizePanel);
currentNode.addEventListener('mouseup', this.stopResize);
currentNode.addEventListener('mouseleave', this.stopResize);
this.setState({ initialY: currentNode.getBoundingClientRect().top })
}
Then, in the event handler, the setting of the height property will be:
this.setState(prevState => ({ height: event.clientY - prevState.initialY }))

Categories