This is quite difficult to explain so I have created this codesandbox to illustrate the problem.
I am working on a package that basically is a wrapper around mousetrap so you can add keyboard events to either the document object or a specific element.
I am testing it out with this code:
const boxes = [{ x: 0, y: 0 }, { x: 0, y: 0 }, { x: 0, y: 0 }, { x: 0, y: 0 }, { x: 0, y: 0 }].map(
(b): Box => {
return { ...b, color: `hsl(${Math.random() * 360}, 100%, 50%)` };
}
);
export const App: React.FC = () => {
const [boxState, setState] = useState(boxes);
const handleMove = (newPosition: Partial<Point>, index: number) => {
setState((state) => {
return state.map((box, i) => {
return index === i ? { ...box, ...newPosition } : { ...box };
});
});
};
return (
<div>
<h1>Click on any box and use arrow keys or WSAD</h1>
{boxState.map(({ x, y, color }, index) => (
<MovableBox key={index} color={color} index={index} x={x} y={y} onMoveRequest={handleMove} />
))}
</div>
);
};
In the code sandbox, you can move the squares down by either pressing the D button in each square of if you click a square, the div gets focus and the arrow keys should move the boxes around.
The problem is that it works fine when pressing the button but when you use the arrow key, the new state is not persisted. It always starts at it's initial value.
The Box component looks like this:
export const Box: React.FC<BoxType & BoxProps> = ({ x, y, color, index, onMoveRequest }) => {
const style: CSSProperties = {
width: '100px',
height: '100px',
backgroundColor: color,
textAlign: 'center',
lineHeight: '100px',
position: 'absolute',
top: `${y + index * 120}px`,
left: `${x + index * 120}px`
};
if (index === 0) {
console.log({ x, y });
}
const SHIFT = 10;
const handleMove = (action) => {
console.log(index);
switch (action) {
case 'MOVE_LEFT':
onMoveRequest({ x: x - SHIFT }, index);
break;
case 'MOVE_RIGHT':
onMoveRequest({ x: x + SHIFT }, index);
break;
case 'MOVE_UP':
onMoveRequest({ y: y - SHIFT }, index);
break;
case 'MOVE_DOWN':
onMoveRequest({ y: y + SHIFT }, index);
break;
default:
throw new Error('Unknown action');
}
};
return (
<Shortcuts shortcutMap={shortcutMap} mapKey="BOX" handler={handleMove} scoped>
<div style={style}>
{index + 1} ({x}, {y})
<button type="button" onClick={() => handleMove('MOVE_DOWN')}>
D
</button>
</div>
</Shortcuts>
);
};
I cannot for the life of me work out why it is different from the keyboard event.
Shortcutsnot receiving the updatedxandy. You are only sendinghandleMovetoShortcutswhich does not acceptxorybut takes it from props. Try to pass 'x' and 'y' toShortcutsand then call the shortcut action with that newxandy-handleMove = (action, newX, newY) =>