Skip to content

Instantly share code, notes, and snippets.

@jongacnik
Last active October 24, 2018 19:07
Show Gist options
  • Save jongacnik/747331e0fccc8d7c107b46498ffbd6fa to your computer and use it in GitHub Desktop.
Save jongacnik/747331e0fccc8d7c107b46498ffbd6fa to your computer and use it in GitHub Desktop.
lazy-nanocomponent

Basic shell for a lazy nanocomponent. Currently scaffolded for Content State pattern, but data could be sourced from anywhere just by updating the fetchData method.

When adding more complex behavior (like a slideshow), I tend to add init and destroy methods. I will call init in onLoaded and destroy in update. This allows for initializing and cleaning up any dom thrashing stuff.

I will also then use the enter and exit methods to do perf tasks, like playing/pausing a slideshow.

var Nanocomponent = require('nanocomponent')
var html = require('nanohtml')
var onIntersect = require('on-intersect')
module.exports = class LazyComponent extends Nanocomponent {
constructor (id, state, emit) {
super(id)
this.state = state || {components:{}}
this.local = this.state.components[id] = {
view: 'placeholder',
visible: false,
url: undefined
}
this.onEnter = this.onEnter.bind(this)
this.onLeave = this.onLeave.bind(this)
}
load (element) {
this.stopObserving = onIntersect(element, this.onEnter, this.onLeave)
}
unload () {
if (this.stopObserving) this.stopObserving()
}
onEnter () {
console.log('enter')
this.local.visible = true
if (this.local.view !== 'loaded') {
this.fetchData()
}
}
onLeave () {
console.log('exit')
this.local.visible = false
}
fetchData () {
console.log('fetch data')
var p = this.state.content[this.local.url]
if (p) {
console.log('loaded')
this.local.view = 'loaded'
if (this && this.element) this.rerender()
} else {
console.log('loading')
this.local.view = 'loading'
if (this && this.element) this.rerender()
setTimeout(() => {
console.log('loaded')
this.local.view = 'loaded'
if (this && this.element) this.rerender()
}, 3000)
}
}
onLoaded () {
this.init()
}
init () {
console.log('do init stuff')
}
destroy () {
console.log('do teardown stuff')
}
afterupdate () {
console.log(this.local)
if (this.local.view === 'loaded') {
this.onLoaded()
}
}
viewPlaceholder () {
return html`<div>Not yet in view</div>`
}
viewLoading () {
return html`<div>Loading data</div>`
}
viewError () {
return html`<div>Error loading data</div>`
}
viewLoaded () {
return html`<div>Loaded</div>`
}
createElement (props = {}) {
Object.assign(this.local, props)
return this.local.view === 'loaded'
? this.viewLoaded()
: this.local.view === 'loading'
? this.viewLoading()
: this.local.view === 'error'
? this.viewError()
: this.viewPlaceholder()
}
update (props = {}) {
if (props.url !== this.local.url) {
this.local.view = 'placeholder'
this.destroy()
Object.assign(this.local, props)
return true
}
}
}
// scaffolded with content state fetch
var Nanocomponent = require('nanocomponent')
var html = require('nanohtml')
var onIntersect = require('on-intersect')
module.exports = class LazyComponent extends Nanocomponent {
constructor (id, state, emit) {
super(id)
this.state = state || {components:{}}
this.local = this.state.components[id] = {
view: 'placeholder',
visible: false,
url: undefined
}
this.onEnter = this.onEnter.bind(this)
this.onLeave = this.onLeave.bind(this)
}
load (element) {
this.stopObserving = onIntersect(element, this.onEnter, this.onLeave)
}
unload () {
if (this.stopObserving) this.stopObserving()
}
onEnter () {
console.log('enter')
this.local.visible = true
if (this.local.view !== 'loaded') {
this.fetchData()
}
}
onLeave () {
console.log('exit')
this.local.visible = false
}
fetchData () {
console.log('fetch data')
var p = this.state.content[this.local.url]
if (p) {
console.log('loaded')
this.local.view = 'loaded'
if (this && this.element) this.rerender()
} else {
console.log('loading')
this.local.view = 'loading'
if (this && this.element) this.rerender()
// fetch into content state
this.contentStateFetch()
}
}
contentStateFetch () {
fetch(this.state.constants.API + this.local.url)
.then(response => response.json())
.then(json => {
Object.keys(json).forEach(key => {
var p = this.state.content[key]
if (!p || (p && !p._loaded)) {
this.state.content[key] = json[key]
}
})
console.log('loaded')
this.local.view = 'loaded'
if (this && this.element) this.rerender()
})
}
onLoaded () {
this.init()
}
init () {
console.log('do init stuff')
}
destroy () {
console.log('do teardown stuff')
}
afterupdate () {
console.log(this.local)
if (this.local.view === 'loaded') {
this.onLoaded()
}
}
viewPlaceholder () {
return html`<div>Not yet in view</div>`
}
viewLoading () {
return html`<div>Loading data</div>`
}
viewError () {
return html`<div>Error loading data</div>`
}
viewLoaded () {
return html`<div>Loaded</div>`
}
createElement (props = {}) {
Object.assign(this.local, props)
return this.local.view === 'loaded'
? this.viewLoaded()
: this.local.view === 'loading'
? this.viewLoading()
: this.local.view === 'error'
? this.viewError()
: this.viewPlaceholder()
}
update (props = {}) {
if (props.url !== this.local.url) {
this.local.view = 'placeholder'
this.destroy()
Object.assign(this.local, props)
return true
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment