【问题标题】:I can't change variable from function on event listener in React我无法从 React 事件侦听器上的函数更改变量
【发布时间】:2020-05-16 05:43:02
【问题描述】:

我有一个窗口mousemove 事件侦听器,它调用一个更改变量的函数。但是,当我将变量分配给道具时,它不会改变:

  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)

我尝试在事件监听器中直接改

(window.addEventListener('mousemove', //everything in cursor) 但是我将无法访问 e 变量。

我也不能将它与 state 一起使用,因为由于某种原因它变得过于滞后和崩溃。

我怎样才能做到这一点?提前致谢。

这就是我在 vscode 中看到的:

(这是在App组件中,变量和事件监听器也在App中。)

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

【问题讨论】:

  • 请为此使用codesandbox.io 创建一个small demo 以显示正在发生的问题。
  • console.log() 正在打印坐标。它正在工作
  • 添加了一个沙盒
  • 沙盒似乎是默认的初始反应模板。您是否保存了您的编辑?
  • 那里没有任何状态变量。如果任何状态的值或道具的值发生变化,那么只有 react 会重新渲染组件。在您的情况下, x & y 只是变量。如果您将它们设为状态变量,那么它将再次重新渲染。

标签: javascript reactjs


【解决方案1】:

组件仅在状态或道具更新时重新渲染。 App 没有状态或道具,因此它永远不会重新渲染,因此子 Cursor 永远不会重新渲染。

您可以使用附加到Cursor 的引用并直接设置顶部和左侧属性。请不要忘记在组件卸载时也删除事件侦听器。

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>
  );
}

编辑

正如@KirillSkomarovskiy 所指出的,使用状态并不是导致页面崩溃和崩溃的原因。我怀疑它是/是添加了多个/重复的 mousemove 处理程序,这些处理程序没有被正确清理(可能通过记录每个更新的位置来复合)。

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>
  );
}

【讨论】:

  • @Roy 这个很有趣,想弄清楚并开始工作。谢谢。
  • 你为什么用useRef而不是useState
  • @KirillSkomarovskiy 也就是说,正如 PO 指出的那样,跟踪所有 mousemove 事件试图更新组件的状态会使 javascript 线程不堪重负,当你抛出在每个事件的 console.log 中。使用useState 的性能很差。由于组件并没有真正“渲染”任何东西,它更像是一个容器。 ref 允许访问底层 DOM 节点,我们只是在更新“光标”div 的位置。
  • @DrewReese 我比较了useRefuseState 并没有注意到任何性能差异。 here 就是我的例子。
  • @KirillSkomarovskiy 哇,你说得对。我返回并使用状态从 OP 的沙箱中重新实现它,我认为之前发生在我身上的事情是我没有进行其他优化来删除侦听器(多个侦听器正在运行)并且仍然控制台记录坐标。 2jqov.csb.app
【解决方案2】:

变量xy 永远不会被有效更新,因为cursor() 是一个以functional 方式工作的ES6 箭头函数,所以如果你想将值从cursor() 传递到xy,那么您也应该将它们视为函数:

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

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

现在您可以将值传递给xy

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

然后你的事件监听器调用cursor(e):

window.addEventListener('mousemove', cursor);

无论您想在哪里使用xy 的值,您只需将它们称为:x()y()

这就是ES6 Functional Programming 的工作原理!

【讨论】:

    猜你喜欢
    • 2018-02-17
    • 1970-01-01
    • 2014-01-07
    • 2012-01-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多