Skip to content

Instantly share code, notes, and snippets.

@norbert-codes
Forked from anonymous/fiddle.babel + jsx
Created March 14, 2018 19:02
Show Gist options
  • Select an option

  • Save norbert-codes/33b17884fd44a88d702bcbc48850d305 to your computer and use it in GitHub Desktop.

Select an option

Save norbert-codes/33b17884fd44a88d702bcbc48850d305 to your computer and use it in GitHub Desktop.
RenderOnVisible (source: https://jsfiddle.net/developit/vLbngh73/)
const { h, Component, render } = preact; /** @jsx h */
let q = [];
function enqueue(fn, delay) {
if (q.push(fn)==1) setTimeout(processQueue, delay);
}
function processQueue() {
let p;
while (p=q.pop()) p();
}
class RenderOnVisible extends Component {
static defaultProps = {
fallback: <div />,
debounce: 50,
visible: false
};
state = {
visible: this.props.visible
};
queueCheck = () => {
if (!this.queued) {
this.queued = true;
enqueue(this.check, this.props.debounce);
}
};
check = () => {
this.queued = false;
if (this.state.visible) {
this.unhook();
return true;
}
let bbox = this.base.getBoundingClientRect(),
viewportHeight = window.innerHeight || document.documentElement.offsetHeight;
if (bbox.top<=viewportHeight && bbox.bottom>=0) {
this.unhook();
this.setState({ visible:true });
return true;
}
};
unhook() {
removeEventListener('scroll', this.queueCheck);
removeEventListener('resize', this.queueCheck);
}
componentDidMount() {
if (!this.check()) {
addEventListener('scroll', this.queueCheck);
addEventListener('resize', this.queueCheck);
}
}
componentWillUnmount() {
this.unhook();
}
render({ fallback, children }, { visible }) {
let child = children[0];
if (typeof child==='function') return child(visible);
return visible ? child : fallback;
}
}
class DemoWidget extends Component {
componentWillMount() {
//console.log(this.props.id+' mounting');
}
componentDidMount() {
setTimeout( () => this.setState({ mounted:true }), 10);
}
componentWillUnmount() {
console.log(this.props.id+' UNmounting');
}
render({ id }, { mounted=false }) {
return (
<div class="widget" mounted={mounted}>
{id}
</div>
);
}
}
const Demo = () => {
let kids = [];
for (let i=1; i<=100; i++) {
kids.push(
i>90 ? (
// example: manually specify initial visibility:
<RenderOnVisible visible>
<DemoWidget id={i} />
</RenderOnVisible>
) : i<=50 ? (
// example: function-as-children to control it yourself
<RenderOnVisible>
{ visible => (
visible ? <DemoWidget id={i} /> : <div class="fallback" />
) }
</RenderOnVisible>
) : (
// example: default compositional behavior with fallback option
<RenderOnVisible fallback={<div class="fallback" />}>
<DemoWidget id={i} />
</RenderOnVisible>
)
);
}
return <div class="demo">{kids}</div>;
};
render(<Demo />, document.body);
html, body {
font: 16px/1.21 'Helvetica Neue',helvetica,arial,sans-serif;
font-weight: 300;
}
.demo {
margin: 25px;
padding: 25px;
background: #DDD;
}
.fallback {
position: relative;
padding: 20px;
margin: 20px;
height: 60px;
}
.widget {
position: relative;
padding: 20px;
margin: 20px;
background: burlywood;
box-shadow: 0 1px 5px rgba(0,0,0,0.3);
font-size: 50px;
color: #555;
text-align: center;
&[mounted] {
transition: background 1s ease;
background: #FFF;
&:after {
content: 'MOUNTED';
position: absolute;
display: block;
left: 0;
width: 100%;
bottom: 10px;
color: #999;
font-size: 12px;
animation: bounce .5s cubic-bezier(.5,0,.1,1.5) forwards 1;
}
}
}
@keyframes bounce {
from { transform: scale(0.01); }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment