3

I have a window mousemove event listener, that calls a function that changes a variable. However when I assign the varibale to a prop it doesn't change:

  var x = null
  var y = null

  const cursor = (e) => {
    x = e.screenX + 'px'
    y = e.screenY + 'px'
    console.log(x, y)
    return x, y
  }

  window.addEventListener('mousemove', cursor)

I tried to directly change it in the event listener

(window.addEventListener('mousemove', //everything in cursor) but then I wouldn't be able to access the e variable.

I also can't use it with state because for some reason it's gets too laggy and crashes.

How can I make this work? Thanks in advance.

That's what I see in vscode:

enter image description here

(This is in the App component, the variables and the evenet listener are in the App too.)

Sandbox: https://codesandbox.io/s/vigorous-agnesi-9l1ic?file=/src/App.js

14
  • 2
    Please create a small demo for this using codesandbox.io to show the issue happening. Commented May 16, 2020 at 5:50
  • console.log() is printing co-ordinates. it's working Commented May 16, 2020 at 5:52
  • Added a sandbox Commented May 16, 2020 at 5:53
  • Sandbox appears to be the default initial react template. Did you save your edits? Commented May 16, 2020 at 5:56
  • 1
    You dont have any state variables there. If any state's value or props's value changes then only react will rerender components. In your case x & y are just variables. If you make them as state variables then it will rerender again. Commented May 16, 2020 at 6:36

2 Answers 2

2

Components only re-render when state or props update. App has no state, or props so it never re-renders, thus the child Cursor never re-renders.

You can use a ref attached to Cursor and set the top and left properties directly. Please don't forget to also remove the event listener when the component unmounts.

import React, { useEffect, useRef } from "react";
import "./styles.css";
import styled from "styled-components";

const Cursor = styled.div`
  height: 30px;
  width: 30px;
  border-radius: 50%;
  border: 1.5px solid black;
  position: absolute;
`;

export default function App() {
  const posRef = useRef(null);

  const cursor = e => {
    const { clientX = 0, clientY = 0 } = e;

    posRef.current.style.left = clientX + "px";
    posRef.current.style.top = clientY + "px";

    // console.log(clientX, clientY);
  };

  useEffect(() => {
    window.addEventListener("mousemove", cursor);
    return () => window.removeEventListener("mousemove", cursor);
  }, []);

  return (
    <div className="App">
      <h1>Demo</h1>
      <Cursor ref={posRef} />
    </div>
  );
}

Edit custom cursor

EDIT

As pointed out by @KirillSkomarovskiy, using state wasn't what was bogging down and crashing the page. I suspect it is/was the adding of multiple/duplicate mousemove handlers that weren't being cleaned up properly (possibly compounded by logging each updated position).

const Cursor = styled.div`
  height: 30px;
  width: 30px;
  border-radius: 50%;
  border: 1.5px solid black;
  position: absolute;
  transform: translate(-50%, -50%);
  top: ${props => props.yPos};
  left: ${props => props.xPos};
`;

export default function App() {
  const [pos, setPos] = useState({ x: 0, y: 0 });

  useEffect(() => {
    const cursor = e => {
      setPos({
        x: e.clientX + "px",
        y: e.clientY + "px"
      });
      // console.log(e.clientX, e.clientY);
    };
    window.addEventListener("mousemove", cursor);
    return () => window.removeEventListener("mousemove", cursor);
  }, []);

  return (
    <div className="App">
      <h1>Demo</h1>
      <Cursor xPos={pos.x} yPos={pos.y} />
    </div>
  );
}

Edit cursor demo

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

4 Comments

Why do you use useRef instead of useState?
@KirillSkomarovskiy Namely that, as the PO points out, tracking all the mousemove events and trying to update a component's state overwhelms the javascript thread, and it's even worse when you throw in the console.log of each event. Using useState had very poor performance. Since the component isn't really "rendering" anything it's more of a container. The ref allows access to the underlying DOM node and we're just updating the position of a "cursor" div.
@DrewReese I've compared useRef and useState and haven't noticed any performance difference. here is my example.
@KirillSkomarovskiy Wow, you're absolutely right. I went back and reimplemented it from the OP's sandbox using state and I think what was happening to me previously is I hadn't made the other optimizations to remove listeners (multiple listeners running) and still console logging the coords. 2jqov.csb.app
-1

The variables x and y will never effectively be updated because cursor() is an ES6 arrow function which works in a functional way, so if you want to pass values from cursor() to x or y, then you should consider them as functions as well:

x = (callback) =>{
   return callback;
}

y = (callback) =>{
   return callback;
}

Now you can pass values to x and y:

const cursor = (e) => {
    x (e.screenX + 'px');
    y (e.screenY + 'px');
}

Then your Event Listener calls cursor(e):

window.addEventListener('mousemove', cursor);

Wherever you want to use the values of x and y you just need to call them like: x() or y().

This is how ES6 Functional Programming works!

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.