Last active
April 28, 2020 23:43
-
-
Save hkjpotato/43b1e53789030883e4ba82cccf873112 to your computer and use it in GitHub Desktop.
why react-intersection-observer is made in that way?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
my naive hook | |
- in the hook itself I always instantiate an observer instance, otherwise I dont know where to hold it | |
- also I pass in a sepefic callback used just for the current comp | |
- the callback detects if is intersecting, if so call a function right in side the component scope, and also unobserve if trigger only once | |
I do this because I feel like everything is local, and can only be assessed locally | |
Why I think each item needs its own observer? Because to instantiate an observer I need to pass in a callback. This callback | |
must be defined during the instantiation. However, at that momement I dont even know which items to be observed. | |
Even if I know the children when instantiate it (like putting at a parent comp's componentDidMount and use some selector to | |
collect the dom, I feel like the nested layers betweeen parent child might make the update unclear. | |
Overall, my mind is limited by the 'scope'. | |
Class, function can create scope..but dont forget the js module file itself is a native scope. | |
Use it smartly to hold references of sharable objects. | |
*/ | |
function useNaiveInView(triggerOnce) { | |
const ref = useRef(); | |
const [inView, setInView] = useState(false); | |
useEffect(() => { | |
const observer = new IntersectionObserver((entries) => { | |
entries.forEach((entry) => { | |
if (entry.isIntersecting) { | |
setInView(true); // in the scope | |
if (triggerOnce) { | |
observer.unobserve(ref.current); | |
} | |
} | |
}); | |
}); | |
observer.observe(ref.current); | |
}, []); | |
return [ref, inView]; | |
} | |
/** | |
* this guy has extract that concern of scope | |
* he writes a generic callback | |
* which does not rely on local scope but a file(module) scope | |
* | |
*/ | |
let observer = null; | |
const instances = new Map(); | |
// observe is like a setter to access this internal state | |
function observe(ele, callback) { | |
if (!observer) { | |
const observer = new IntersectionObserver((entries) => { | |
entries.forEach((entry) => { | |
// if (entry.isIntersecting) | |
// let inView = entry.isIntersecting; | |
// instance.callback | |
if (entry.isIntersecting) { | |
const instance = instances.get(entry.target); | |
instance.callback(true); | |
} | |
}) | |
}); | |
} | |
const instance = { | |
callback: callback, | |
}; | |
instances.set(ele, instance); | |
observer.observe(ele); | |
} | |
function destroy() { | |
observer.disconnect(); | |
instances.clear(); | |
} | |
function unobserve(ele) { | |
observer.unobserve(ele); | |
instances.delete(ele); | |
if (instances.size === 0) { | |
observer.disconnect(); | |
} | |
} | |
function useInView(triggerOnce) { | |
const [inView, setInview] = useState(false); | |
const setRef = useCallback((node) => { | |
// https://reactjs.org/docs/refs-and-the-dom.html#callback-refs | |
if(node) { | |
observe(node, (inView) => { | |
setInview(inView) | |
if (triggerOnce) { | |
unobserve(node) | |
} | |
}) | |
} | |
},[]); | |
return [setRef, inView]; | |
} | |
//https://gist.github.com/thebuilder/fb07c989093d4a82811625de361884e7 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment