hzdg / hz-core

HZ's internal library of React Components 🚧
https://hz-core.netlify.com
0 stars 0 forks source link

[hook-intersection] intersects state should be based on `intersectionRatio` and `isIntersecting` #32

Open lettertwo opened 4 years ago

lettertwo commented 4 years ago

Description

Using this hook statefully, like:

const [intersects, ref] = useIntersection();

provides a boolean intersects value that is derived from the isIntersecting value of the underlying IntersectionObserverEntry.

In the simple case (as above), this is fine, but if thresholds are defined, expectations change:

const [intersects, ref] = useIntersection({threshold: 0.75});

Here, the expectation is that intersects will be true only if the intersectionRatio is 0.75 or greater.

This works as expected in Chrome, but not in Firefox. As it turns out, this may be due to a chromium bug. Interestingly, MDN suggests that the chromium behavior is correct, but it seems the spec actually sides with Firefox.

Steps to Reproduce

View the stateful example in Firefox.

Expected Result

The example stays red until the intersection ratio meets the provided threshold.

Actual Result

The example turns green as soon as any intersection occurs. This results in the example always being green, but if you modify the example to move the element out of the containing view, you can see in the DOM that it switches to red.

Additional Context

We most likely need to update our intersects state calculation to compare the provided thresholds to intersectionRatio, not just base it on isIntersecting. Note that it's possible to define multiple thresholds, like:

const [intersects, ref] = useIntersection({threshold: [0.5, 0.6, 0.7, 0.8, 0.9, 1]});

so the calculation must account for this case.

presto2116 commented 4 years ago

@lettertwo Looking into this issue now. For goodwill-halloween my solution was to use the intersectionRatio and set my state inside of the hook. Maybe instead of returning boolean isIntersecting. we return the ratio and let the user handle the threshold. Chrome and Firefox both process intersectionRatio correctly but do not process isIntersecting the same way.

  const [intersectionState, setIntersectionState] = useState({
    intersectionRatio: 0,
    boundingClientRect: 0,
  });

useIntersection(
    ref,
    ({ intersectionRatio, boundingClientRect }) => {
      setIntersectionState({ intersectionRatio, boundingClientRect });
    }
  );

instead of

const [intersects, ref] = useIntersection({threshold: [0.5, 0.6, 0.7, 0.8, 0.9, 1]});

return the intersectionRatio

const [intersectionRatio, ref] = useIntersectionRatio();

and have the user use that accordingly. Looking through the code, it doesn't seem like much would change. you can still pass in config and handle stateful/stateless examples. and it eliminates the need for passing in different thresholds into the hook. It is a pain that a simple boolean wont be returned, but the boolean doesn't even work in this case so updating everything will be for the better. Thoughts?