What I am currently developing
I am using React hook to develop the function to reposition the DOM by drag and drop.
The code up to the halfway point can be found at this link.
https://codesandbox.io/s/exciting-williams-z4ngdz?file=/src/App.js
The code in question
I want to update the value of useRef() in React onMouseDown event, but I cannot update it and it remains at the initial value.
import axios from 'axios';
import React, { useEffect, useState, useRef } from 'react';
import './App.css';
const baseURL = new URL('https://api.ks-portfolio.site/skills');
/**
* @return {Promise<{Object}>}
*/
const apiGet = async () => await axios.get(baseURL);
/**
* @param {MouseEvent} event
* @param {HTMLElement} element
* @return {boolean}
*/
const isHover = (event, element) => {
const clientX = event.clientX;
const clientY = event.clientY;
const rect = element.getBoundingClientRect();
return clientY < rect.bottom && clientY > rect.top && clientX < rect.right && clientX > rect.left;
};
function App() {
const [blocks, setBlocks] = useState([]);
// Manage status with ref.
const state = useRef({
dndItems: [],
keys: new Map(),
dragElement: null,
canCheckHovered: true,
pointerPosition: { x: 0, y: 0 },
sortIds: [],
}).current;
const sortBlocks = (sortItems) => {
const canCheckTime = 300;
/**
* @param {MouseEvent} event
* @returns
*/
const onMouseMove = (event) => {
const { clientX, clientY } = event;
const { dndItems, dragElement, pointerPosition } = state;
if (!dragElement) return;
const x = clientX - pointerPosition.x;
const y = clientY - pointerPosition.y;
const dragStyle = dragElement.element.style;
dragStyle.zIndex = '100';
dragStyle.cursor = 'grabbing';
dragStyle.transform = `translate(${x}px,${y}px)`;
if (!state.canCheckHovered) return;
state.canCheckHovered = false;
setTimeout(() => state.canCheckHovered = true, canCheckTime);
const dragIndex = dndItems.findIndex(({ key }) => key === dragElement.key);
const hoveredIndex = dndItems.findIndex(({ element }, index) => index !== dragIndex && isHover(event, element));
if (hoveredIndex !== -1) {
state.pointerPosition.x = clientX;
state.pointerPosition.y = clientY;
dndItems.splice(dragIndex, 1);
dndItems.splice(hoveredIndex, 0, dragElement);
const { left: x, top: y } = dragElement.element.getBoundingClientRect();
dragElement.position = { x, y };
setBlocks(dndItems.map((item) => item.value));
}
};
/**
* @return {void}
*/
const onMouseUp = () => {
const { dragElement } = state;
if (!dragElement) return;
const dragStyle = dragElement.element.style;
dragStyle.zIndex = '';
dragStyle.cursor = '';
dragStyle.transform = '';
state.dragElement = null;
window.removeEventListener('mouseup', onMouseUp);
window.removeEventListener('mousemove', onMouseMove);
};
return sortItems.map((value) => {
const key = state.keys.get(`item_${value.language_id}`) || Math.random().toString(16);
state.keys.set(`item_${value.language_id}`, key);
if (!state.sortIds.includes(value.language_id)) {
state.sortIds.push(value.language_id);
}
return {
value,
key,
events: {
ref: (element) => {
const { dndItems, dragElement, pointerPosition, sortIds } = state;
if (!element || !dragElement) return;
const block = element.closest('.block');
block.style.transform = '';
const { left: x, top: y } = block.getBoundingClientRect();
const position = { x, y };
const itemIndex = dndItems.findIndex((item) => item.key === key);
if (itemIndex === -1 && sortIds.length > dndItems.length) {
return dndItems.push({ key, value, element: block, position });
}
if (dragElement.key === key) {
const dragX = dragElement.position.x - position.x;
const dragY = dragElement.position.y - position.y;
block.style.transform = `translate(${dragX}px,${dragY}px)`;
pointerPosition.x -= dragX;
pointerPosition.y -= dragY;
}
if (dragElement.key !== key) {
const item = dndItems[itemIndex];
const x = item.position.x - position.x;
const y = item.position.y - position.y;
block.style.transition = '';
block.style.transform = `translate(${x}px,${y}px)`;
requestAnimationFrame(() => {
block.style.transform = '';
block.style.transition = `all ${canCheckTime}ms`;
});
}
state.dndItems[itemIndex] = { key, value, element: block, position };
},
/**
* @param {React.MouseEvent<HTMLElement>} event
* @return {void}
*/
onMouseDown: (event) => {
const element = event.currentTarget.closest('.block');
state.pointerPosition.x = event.clientX;
state.pointerPosition.y = event.clientY;
element.style.transition = '';
element.style.cursor = 'grabbing';
const { left: x, top: y } = element.getBoundingClientRect();
const position = { x, y };
state.dragElement = { key, value, element, position };
console.log('No value is stored in dragElement.')
console.log('onMouseDown', {key, value, element, position});
console.log('state', state);
window.addEventListener('mouseup', onMouseUp);
window.addEventListener('mousemove', onMouseMove);
},
},
};
});
};
useEffect(() => {
async function fetchData() {
const response = await apiGet();
setBlocks(response.data[0].programming);
}
fetchData();
}, []);
return (
<ul className="App">
{sortBlocks(blocks).map((block,i) => {
const { language } = block.value;
return (
<li key={i} className="block">
<div className="panel-body" {...block.events}>
{language}
</div>
</li>
)
})}
</ul>
);
}
export default App;
Corresponding code
The dragElement on line 158 is null.
return sortItems.map((value) => {
events: {
ref: (element) => {
const { dndItems, dragElement, pointerPosition, sortIds } = state;
console.log("events", dragElement); // dragElement = null
}
}
Please let me know what the problem is and how to deal with it.