首页 > 解决方案 > React hooks, hammerjs with closures

问题描述

In a feature request, I needed to detect swipe events and change something on dom accordingly. So I decided to use hammerjs and give it a shot for react hooks. But I didn't understand how the closures actually work in code below. Can someone explain why left & right handlers are always using first value of state object

tl;dr: Can't change counter properly with swipes. Goes only to -1 or 1 min max

const App = () => {
  const elm = useRef(null);
  const [counter, setCounter] = useState(0);
  const [obj, setObj] = useState({ counter: 0 });
  const [mounted, setMounted] = React.useState(false);

  React.useLayoutEffect(() => {
    if (!mounted && elm.current) {
      const h = new Hammer(elm.current);
      h.on("swipeleft", () => setCounter(counter + 1));
      h.on("swiperight", () => setCounter(counter - 1));
      h.on("swipeleft", () => setObj({ counter: obj.counter + 1 }));
      h.on("swiperight", () => setObj({ counter: obj.counter - 1 }));
      setMounted(true);
    }
  });

  return (
    <div
      ref={elm}
      style={{ width: "300px", height: "300px", backgroundColor: "orange" }}
    >
      <p>This is a swipeable content area</p>
      <p>Counter: {counter}</p>
      <p>Obj counter: {obj.counter}</p>
    </div>
  );
};

Here is codesandbox https://codesandbox.io/s/l911vj98yz

标签: javascriptreactjsreact-hooks

解决方案


Hammer 对象的回调范围仅在第一次渲染时捕获一次。值是否更改无关紧要,范围与初始值一起保留。为避免此问题,您可以使用函数形式来更新您的值。

h.on("swipeleft", () => setCounter(previousCounter => previousCounter + 1);

推荐阅读