react-use icon indicating copy to clipboard operation
react-use copied to clipboard

useScroll, useScrolling, useHoverDirty does not work when used on initially unmounted element

Open hastom opened this issue 6 years ago β€’ 6 comments

What is the current behavior?

When referenced component initially unmounted, and mounted later (e.g on click) useScroll, useScrolling, useHoverDirty hooks have no effect.

Steps to reproduce it and if possible a minimal demo of the problem. Your bug will get fixed much faster if we can run your code and it doesn't have extra dependencies other than react-use. Paste the link to your JSFiddle or CodeSandbox example below:

https://codesandbox.io/s/react-use-issue-ry7zk

What is the expected behavior?

Hooks should fire when component is mounted and do not when it is unmounted

A little about versions:

  • OS: MaxOS 10.15
  • Browser (vendor and version): Chrome 78
  • React: 16.8.4
  • react-use: 12.9.1
  • Did this worked in the previous package version? idk

I checked sources and saw there is [ref] object in deps of useEffect hook. It's pointless cause ref is mutable by design and it will not report any changes

hastom avatar Oct 30 '19 11:10 hastom

It is a confusing scene for useRef. ref should never rerun effect or render when ref.current changed. So, you can solve it by moving these hooks inside a new sub-component. For more, if we need to measure ref.current, we should use useState and callback instead of useRef.

qianL93 avatar Oct 31 '19 02:10 qianL93

I solved my problem by writing my own hook with useState and callback as you say. It causes single additional rerender but that’s ok for me. You can also achieve 0 additional rerenders if you provide createRef callback with hook and will attach/detach events inside it. But it will be a little less flexible.

Still a bug, I guess, cause having null in ref.current is valid behavior. And sometimes you cannot just change it (e.g integrating with 3d party staff)

hastom avatar Oct 31 '19 04:10 hastom

Is this a bug? As suggested by @qianL93 , it can be solved by moving these hooks to a new child component. This issue should be closed, in my opinion.

manishsundriyal avatar Apr 13 '20 11:04 manishsundriyal

It is a confusing scene for useRef. ref should never rerun effect or render when ref.current changed. So, you can solve it by moving these hooks inside a new sub-component. For more, if we need to measure ref.current, we should use useState and callback instead of useRef.

@qianL93 could you explain this further or provide a minimal codesandbox?

mikemajara avatar Jan 28 '21 09:01 mikemajara

having the same issue here. useScroll always return 0

aelsaman avatar Jun 16 '22 14:06 aelsaman

Any solution?

lucasbara avatar Jun 23 '22 13:06 lucasbara

Can I take this?

sweetliquid avatar Nov 01 '22 16:11 sweetliquid

@aelsaman @lucasbara @sweetliquid @mikemajara Here's a simple example:

https://codesandbox.io/s/react-use-issue-forked-9gmzfg?file=/src/index.tsx

It combines an "initially unmounted element" with the useScrolling, useScroll and useHoverDirty hooks to create a child component.

import * as React from "react";
import { render } from "react-dom";
import { useScroll, useScrolling, useHoverDirty } from "react-use";

import "./styles.css";

const Workspace = () => {
  const ref = React.useRef<HTMLDivElement>(null);
  const { x, y } = useScroll(ref);
  const isScrolling = useScrolling(ref);
  const isHovering = useHoverDirty(ref);
  return (
    <div
      ref={ref}
      style={{ height: 400, width: 200, overflow: "auto", border: 1 }}
    >
      <div style={{ position: "fixed" }}>
        positions: {JSON.stringify({ x, y })}
        <br />
        isScrolling: {JSON.stringify(isScrolling)}
        <br />
        hover: {JSON.stringify(isHovering)}
      </div>
      <div style={{ height: 800, width: 200, backgroundColor: "#0FC" }} />
    </div>
  );
};

function App() {
  const [active, setActive] = React.useState(false);
  return (
    <div className="App">
      <button onClick={() => (active ? setActive(false) : setActive(true))}>
        TOGGLE
      </button>
      {active && <Workspace />}
    </div>
  );
}

const rootElement = document.getElementById("root");
render(<App />, rootElement);

fuxieorg avatar Jan 27 '23 23:01 fuxieorg