if (inView && options.triggerOnce) { // If it should only trigger once, unobserve the element after it's inView unobserve(node); } }, options ); }
// Store a reference to the node, so we can unobserve it later ref.current = node; }, [options.threshold, options.root, options.rootMargin, options.triggerOnce] );
// 中间部分 2 useEffect(() => { if (!ref.current && state !== initialState && !options.triggerOnce) { // If we don't have a ref, then reset the state (unless the hook is set to only `triggerOnce`) // This ensures we correctly reflect the current state - If you aren't observing anything, then nothing is inView setState(initialState); } });
exportfunctionobserve( element: Element, callback: ObserverInstanceCallback, options: IntersectionObserverInit = {} ) { // IntersectionObserver needs a threshold to trigger, so set it to 0 if it's not defined. // Modify the options object, since it's used in the onChange handler. if (!options.threshold) options.threshold = 0; const { root, rootMargin, threshold } = options; // Validate that the element is not being used in another <Observer /> invariant( !INSTANCE_MAP.has(element), "react-intersection-observer: Trying to observe %s, but it's already being observed by another instance.\nMake sure the `ref` is only used by a single <Observer /> instance.\n\n%s" ); /* istanbul ignore if */ if (!element) return; // Create a unique ID for this observer instance, based on the root, root margin and threshold. // An observer with the same options can be reused, so lets use this fact letobserverId: string = getRootId(root) + (rootMargin ? `${threshold.toString()}_${rootMargin}` : threshold.toString());
let observerInstance = OBSERVER_MAP.get(observerId); if (!observerInstance) { observerInstance = newIntersectionObserver(onChange, options); /* istanbul ignore else */ if (observerId) OBSERVER_MAP.set(observerId, observerInstance); }
constinstance: ObserverInstance = { callback, element, inView: false, observerId, observer: observerInstance, // Make sure we have the thresholds value. It's undefined on a browser like Chrome 51. thresholds: observerInstance.thresholds || (Array.isArray(threshold) ? threshold : [threshold]), };
// Firefox can report a negative intersectionRatio when scrolling. /* istanbul ignore else */ if (instance && intersectionRatio >= 0) { // If threshold is an array, check if any of them intersects. This just triggers the onChange event multiple times. let inView = instance.thresholds.some((threshold) => { return instance.inView ? intersectionRatio > threshold : intersectionRatio >= threshold; });
if (isIntersecting !== undefined) { // If isIntersecting is defined, ensure that the element is actually intersecting. // Otherwise it reports a threshold of 0 inView = inView && isIntersecting; }
// Check if we are still observing any elements with the same threshold. let itemsLeft = false; // Check if we still have observers configured with the same root. let rootObserved = false; /* istanbul ignore else */ if (observerId) { INSTANCE_MAP.forEach((item, key) => { if (key !== element) { if (item.observerId === observerId) { itemsLeft = true; rootObserved = true; } if (item.observer.root === root) { rootObserved = true; } } }); } if (!rootObserved && root) ROOT_IDS.delete(root); if (observer && !itemsLeft) { // No more elements to observe for threshold, disconnect observer observer.disconnect(); }
// Remove reference to element INSTANCE_MAP.delete(element); } }