0

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.

1
  • I've been playing with this and I am very sure this is an issue with Shortcuts not receiving the updated x and y. You are only sending handleMove to Shortcuts which does not accept x or y but takes it from props. Try to pass 'x' and 'y' to Shortcuts and then call the shortcut action with that new x and y - handleMove = (action, newX, newY) => Commented Jan 13, 2019 at 14:51

1 Answer 1

1

Ok your issue:

You initialize the keyboard shortcuts in the Shortcut component on "didMount". That means it only registers the handler function when it mounts, with the reference to the initial y.

To fix this pass x & y to the Shortcuts component then take everything from componentDidMount and add it to a new method - initializeShortcuts => (props: Props) => {...}.

Run this function in componentDidMount() & componentWillReceiveProps() (or getDerivedState whatever with props & nextProps respectively and it works. Also remember to unbind the shortcuts before binding them again in the latter.

Working version - https://codesandbox.io/s/wnl6v7x69w.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.