choojs / nanocomponent Goto Github PK
View Code? Open in Web Editor NEW๐ - create performant HTML components
Home Page: https://leaflet.choo.io/
License: MIT License
๐ - create performant HTML components
Home Page: https://leaflet.choo.io/
License: MIT License
As per our talk 2 weeks ago, it'd be useful to start thinking of component.use()
.
The goal of this API would be to provide a way to extend components the way we extend choo
with app.use()
.
Not quite sure on the API yet, but got a hunch that using an internal event emitter would be the way to go for this. Input very welcome!
I ran into the problem of images not loading after navigation.
I am not sure if this is a bug or if it's just me not understanding something correctly...
With this little app I was able to reproduce the problem:
const choo = require('choo')
const html = require('choo/html')
const Nanocomponent = require('nanocomponent')
class Image extends Nanocomponent {
constructor (image) {
super()
this.image = image
}
createElement () {
console.log(`rendering ${this.image}`)
return html`<img src="${this.image}" alt="${this.image}" style="width: 100px;">`
}
update () {
return true
}
}
class Viewer extends Nanocomponent {
createElement (state) {
const container = html`<div>
<h3>Images:</h3><p>
<a href="/?img1=1&img2=1&img3=1">back to the overview</a>
</p></div>`
if(state.query.img1)
container.append(html`<a href="/?img1=1&img2=1"><p>1:</p>${img1.render()}</a>`)
if(state.query.img2)
container.append(html`<a href="/?img2=1&img3=1"><p>2:</p>${img2.render()}</a>`)
if(state.query.img3)
container.append(html`<a href="/?img3=1&img2=1"><p>3:</p>${img3.render()}</a>`)
return container
}
update () {
return true
}
}
const img1 = new Image('1.jpg')
const img2 = new Image('2.jpg')
const img3 = new Image('3.jpg')
const viewer = new Viewer()
const app = choo()
app.mount('body')
app.route('/index.html', main)
app.route('/', main)
function main(state, emit){
return html`<body>
${viewer.render(state)}
</body>`
}
It shows multiple images, depending on the URL query.
Each image is a link that leads to another page that shows this image and one other. (with the same structure)
Reproducing the problem works in 4 steps:
/
- no image displayed -> click on "overview"/?img1=1&img2=1&img3=1
- all 3 images are displayed -> click on the first/?img1=1&img2=1
- image 1 and 2 are displayed -> click on the second/?img2=1&img3=1
- image 2 should be displayed, but doesn't get loaded, image 3 is okThe html looks like the following (no src and alt property!):
<img data-nanocomponent="ncid-5f7b" id="ncid-5f7b" data-proxy="">
According to the log the component is re-rendered.
The element id stays the same (I assume this is expected).
I don't know much about the internals of choo/nanocomponent, so I am pretty helpless on my own. Thanks very much in advance!
EDIT:
I am using choo and nanocomponent only in the browser using browserify.
Versions:
"choo": "^6.13.3",
"nanocomponent": "^6.5.2"
hey @yoshuawuyts, sweet module!
thinking about using this, but then i tried to apply this to a common pattern.
before using nanocomponent
:
module.exports = function render (model, dispatch) {
return html`
<ul onload=${handleLoad}>
${model.cats.map(cat => html`
<li>${cat.name}</li>
`)}
</ul>
`
function handleLoad (el) {
dispatch(loadCats())
}
}
while using nanocomponent
:
module.exports = nanocomponent({
render: function render (model, dispatch) {
return html`
<ul>
${model.cats.map(cat => html`
<li>${cat.name}</li>
`)}
</ul>
`
},
onload: function handleLoad (el) {
// HALP, i don't have a way to dispatch actions!
dispatch(loadCats())
}
})
or am i missing something? how do you implement this pattern in your code?
Hi ๐
I'm just starting with nanocomponent so this might be a repeated/silly question. I was wondering how can I call a method from a component that change the whole choo app state. Going further, it would be cool to share state between components and choo app, because in the createElement
function I'm always passing some or all of the state.
Maybe there is an easy way to do this, but I just can figure it out.
I am getting strange behavior when I use electron's built-in node integration to require nanocomponent rather than browserify.
When running the nanocomponent examples within electron, 'load' and 'unload' events are never called, even though the element is mounted to and removed from the dom. No errors are logged, and the rest of the events, including 'beforerender' all seem to behave as documented.
i don't know enough about any of the involved projects to know why there would be a difference/whether it is expected!
I can include more information + code if it is helpful =]
Maybe add the release process to CONTRIBUTING
Update changelog date
Bump package.json
commit -m '6.4.0'
git tag v6.4.0
git push ; git push --tags
npx gh-release
npm publish
I put together an example of nanocomponent leaking proxy elements into the real DOM when morphing between two instances of the same component:
https://gist.github.com/bcomnes/fa4fa3a83807b4189356699c17207a0b
Two issues at play here:
on-load
, an undocumented argument that is critical to firing lifecycle methods correctly. (see https://github.com/hypermodules/cache-component/blob/master/index.js#L40 for an example)Want me to PR these fixes to nanocomponent?
Following the suggestion in choojs/choo#290, I'm trying to use nanocomponent to embed a twitter widget.
First attempt:
function TwitterWidget() {
if (!this instanceof TwitterWidget) return new TwitterWidget()
Nanocomponent.call(this)
}
TwitterWidget.prototype = Object.create(Nanocomponent.prototype)
TwitterWidget.prototype._render = id => {
console.log('rendering ' + id)
this._id = id
this._elt = html`<div></div>`
return this._elt
}
TwitterWidget.prototype._update = newId => {
return newId !== this._id
}
TwitterWidget.prototype._load = () => {
console.log('loading ' + this._id)
twttr.widgets.createTweet(this._id, this._elt)
}
const widget = new TwitterWidget()
function embedTweet(id) {
return html`
<div class="row">
<div class="col-xs-12 col-md-8">
${widget.render(id)}
</div>
</div>
`
}
This works as long as there is only one widget. To create more, I tried caching them by tweet ID:
let widgets = {}
function widgetFactory(id) {
if (!widgets[id]) {
console.log('creating new widget for ' + id)
widgets[id] = new TwitterWidget()
}
return widgets[id].render(id)
}
function embedTweet(id) {
return html`
<div class="row">
<div class="col-xs-12 col-md-8">
${widgetFactory(id)}
</div>
</div>
`
}
Unfortunately it didn't work with two widgets because the second widget gets loaded twice. Twitter's widget code also throws an exception. Here is the console output:
bundle.js:217 creating new widget for 11111
bundle.js:198 rendering 11111
bundle.js:217 creating new widget for 22222
bundle.js:198 rendering 22222
bundle.js:210 loading 22222
bundle.js:210 loading 22222
widgets.js:9 Uncaught (in promise) TypeError: Cannot read property 'createElement' of null
at t.createElement (widgets.js:9)
at i (widgets.js:9)
at widgets.js:9
(anonymous) @ widgets.js:9
i @ widgets.js:9
(anonymous) @ widgets.js:9
widgets.js:9 Uncaught (in promise) undefined
(anonymous) @ widgets.js:9
n @ widgets.js:9
widgets.js:9 Uncaught (in promise) undefined
Here is my code if you want to try recreating it: notenoughneon/nekocafe@6be5fcb
Sorry for not having a smaller test case.
There are two ways I use sheetify. One is css() outside of my nanocomponent and the other I'd like to use but having issue with is dynamic classes.
This is the error i get
bundle.js:1 Error: Parsing file component.js: visitor[(override || node.type)] is not a function
when I put
createElement(obj) {
const prefix = css`
:host > h1 {
text-align: center;
}
`;
return html` <div class=${prefix} onclick=${this.click}>
in my nanocomponent. I have it in my createElement before i return some html. It also has the same error if i move that prefix to the constructor. I have no problem if i consume fixed css outside
css("./component.css");
class Component extends Nanocomponent {
Using it this way works just fine. However, I'd like the flexibility of being able to use dynamically generated css through dynamic classes instead of dynamic inline styling, and I was hoping Sheetify would work for this in nanocomponent. i.e. I'd like to do
createElement(obj) {
const prefix = css`
:host > h1 {
text-align: center;
font-size:${obj.fontSize};
}
`;
return html` <div class=${prefix} onclick=${this.click}>
Not sure if this is choo, nanocomponent, or nanocomponent-adapters readme issue. Or an issue with how I'm using those.
Clicking on a list item selects it as expected, but if you click an already-selected item, the list disappears, replaced with <div></div>
. This is the case that I would expect the ol(items, selectedIndex, send)
to recycle the cached element, b/c the arguments don't change.
RequireBin-able:
const component = require('nanocomponent')
const html = require('choo/html')
const choo = require('choo')
const li = component({
render: function (item, index, selectedIndex, send) {
function onClick () {
send('select', index)
}
return html`
<li onclick=${onClick}>
${item}
${index === selectedIndex ? '<-' : ''}
</li>
`
}
})
const ol = component({
render: function (items, selectedIndex, send) {
return html`
<ol>
${items.map(function (item, index) {
return li(item, index, selectedIndex, send)
})}
</ol>
`
}
})
const app = choo()
app.model({
state: {
items: ['one', 'two', 'three', 'four'],
selectedIndex: 0
},
reducers: {
select: function (state, data) {
return {selectedIndex: data}
}
},
})
app.router({default: '/'}, ['/', mainView])
document.body.appendChild(app.start())
function mainView (state, prev, send) {
const {items, selectedIndex} = state
return html`
<section>
${ol(items, selectedIndex, send)}
</section>
`
}
I don't know of a good way to protect private vars from being overwritten. I know @toddself accidentally ran into the situation where he overwrote a private var. Perhaps we could add a list of private vars not to overwrite.
this._hasWindow = typeof window !== 'undefined'
this._id = null // represents the id of the root node
this._ncID = null // internal nanocomponent id
this._proxy = null
this._loaded = false // Used to debounce on-load when child-reordering
this._rootNodeName = null
this._name = name || 'nanocomponent'
this._arguments = []
this._handleLoad = this._handleLoad.bind(this)
this._handleUnload = this._handleUnload.bind(this)
In React 16 components introduce something new called error boundries. The main idea is that if an error is thrown from a component, it can be caught early, and prevents crashing the whole web page.
I find this interesting because:
component.use
(or similar); can send through the error to a local dev logger, or monitoring solutionThoughts?
Too much stuff is failing, we need to make sure this lib works as intended
I'm building an isomorphic app (using choo) where the server renders to string and the clients mounts the application on the existing DOM node. I'm actually using microcomponent but noticed an issue with the underlying mechanisms of nanocomponent. When mounting (using nanomnorph) only the attributes from the client rendered nodes are copied to the existing nodes, this has the effect that the reference to this._element
in nanocomponent is not the element that is in the actual DOM, but that which was used by nanomorph for diffing. This causes any subsequent calls to morph
with this._element
to have no effect since they are morphing an element that is not in the DOM.
I would have made a PR but was unsure where this issue should be addressed.
Here's a requirebin that illustrates the issue (see console for output):
http://requirebin.com/?gist=b82e86737dd91c5a2c80d49e64d30684
Getting the error Uncaught TypeError: CreateListFromArrayLike called on non-object
(line 119) when onupdate
is defined. Looking into it, but thought to drop a quick note here!
component({
onupdate: function (el, data) {
console.log(el, data)
},
render: function (data) {
return h`<div>hello!</div>`
}
})
Should mention this is in use w/ choo, and am running into the same issue with the example.
I'm creating a couple of adapters (for Custom Element API v2 and Angular (2+)) and while looking at the code of nanocomponent
I could only find one place in which render is called:
if (!isRendered) {
isRendered = true
args = _args
element = render.apply(render, args)
onload(element, handleLoad, handleUnload)
}
Once this happens, it seems like it's impossible to re render the component.
Does this mean that the only way to re-render is to actually remove the node and create a new one?
In that case, why do we need the onupdate callback? It looks like there's no way to re-render the component with the updated values (if there is, I'd like to know, maybe I just missed it :D)
Of course we could just do:
onupdate: function (el, args) {
console.log(`totally updating now`);
this.apply(this, [args]);
},
since we call update with render
as the first parameter of the apply fn. But it wouldn't really help since we can't do anything with the returned node.
I guess my question is, is there a way to re-render the same instance with the updated values, other than re creating the component?
A typo on https://github.com/choojs/nanocomponent/blob/master/index.js#L54 means that once this._rerender
is set to true
, it is never changed, and this.update()
is never called again.
There is a decent chance I misunderstood the README, but I was expecting createElement only be called once if update always returned false, to make embedding of other DOM elements easier.
This doesn't always seem to be the case when using multiple components. Here is a test-case:
// save as app.js
var html = require('nanohtml')
var morph = require('nanomorph')
var Button = require('./component.js')
var c1 = new Component('a')
var c2 = new Component('b')
var tick = 0
// render both 10 times
for (var i = 0; i < 10; i++) {
render(c1)
render(c2)
}
// some render function that just updates a counter
function render (c) {
const b = html`<body>
${c.render('hi:' + tick++)}
</body>`
morph(document.body, b)
}
// save as component.js
// This component is the one from the README in the section about mutable components.
// Modified to write out every time createElement is called.
var Nanocomponent = require('nanocomponent')
var html = require('nanohtml')
class Component extends Nanocomponent {
constructor () {
super()
this.text = ''
}
createElement (text) {
console.log('Calling createElement')
this.text = text
return html`<h1>${text}</h1>`
}
update (text) {
if (text !== this.text) {
this.text = text
this.element.innerText = this.text // Directly update the element
}
return false // Don't call createElement again
}
unload (text) {
console.log('No longer mounted on the DOM!')
}
}
module.exports = Component
Running the above app will result in 20 createElements being written out. If only one component is used then it's only written out once. Is that working as expected? If so how would you embed a third party dom element.
For now we've published a fork of nanomorph that supports short circuiting "guarded" elements for this using https://github.com/hyperdivision/nanomorph/blob/master/index.js#L63 and https://github.com/hyperdivision/nanomorph-guard
Thanks for all the great stuff btw! :)
nanohtml
now supports document fragments, which are very handy for returning a collection of siblings without having to wrap them in an arbitrary element.
However nanocomponent
is not currently designed to handle this.
If we modify the el instanceof window.Element
assert in _handleRender
to also accept window.DocumentFragment
, we encounter an error because nanocomponent is trying to brand the returned node (which isn't a node).
This also complicates proxying and many other things. I'm not sure how much of a rewrite this would require, but I do think it would be nice to support this feature.
Thoughts?
Hi!
Noob here so I might just be missing something obvious or complicating things for no reason.
I am in a situation where I have to create a choo component that sets its width
and height
values by first loading an image as a child node, and then using that image to defines width
and height
of the root node. That works.
The problem comes when I add a resize
EventListener
and try to rerender()
the component. If I log the function this.rerender()
I always get undefined.
Some relevant code (file here):
load (el) {
const resize = () => {
if (el.firstChild.firstChild.nodeName.toLowerCase() === 'img') {
this.width = el.firstElementChild.clientWidth
this.height = (el.firstElementChild.firstChild.dataset.height / el.firstElementChild.firstChild.dataset.width) * el.firstElementChild.firstChild.clientWidth
} else {
this.width = el.firstElementChild.firstChild.clientWidth
this.height = (el.firstElementChild.firstChild.firstChild.dataset.height / el.firstElementChild.firstChild.firstChild.dataset.width) * el.firstElementChild.firstChild.firstChild.clientWidth
}
this.rerender.bind(this)()
console.log( this.rerender.bind(this)() )
}
resize()
window.addEventListener('resize', function () {
resize()
})
}
update (el) {
return true
}
and how I create the component (file here):
function entry (state, emit) {
return ov(page[0].children).map(function (item, i) {
var card = state.cache(Card, i)
return card.render(state, emit, item, i, state.cards[i])
})
}
Could it be that state.cache
does not see that I am changing the width
and height
values when resizing the page, so... I should be explicit about that?
Thanks,
af
Hi ๐
I have a situation where I have many components and some external libraries which manipulates the DOM. So, those libraries transform the root node of my components, and I need to run functions from the components after they are manipulated but I can't figure out how to get reference to that instance from the node manipulated for this library.
In other words, what I would like is the inverse of the element
property of nanocomponent, perhaps a function that given a DOM element returns the actual nanocomponent tied to it. Is this possible?
Thanks :)
Apologies for the rough draft here; I didn't have much time to phrase things here properly, but I hope the general idea makes sense. This is mostly so we can start thinking about this problem, and create a concrete starting point to work from.
Some of the more CMS-y applications make heavy use of server rendering, which produces pages that can then be stored on CDNs. In order for components to be rendered on the server, it might be useful if they could define an initial set of data before being rendered.
There's probably a good set of considerations as to the more finer grained details, but I was thinking an API like this might just be the right fit.
// button.js
var Nanocomponent = require('nanocomponent')
var html = require('bel')
function Button () {
if (!(this instanceof Button)) return new Button()
this.color = null
Nanocomponent.call(this)
}
Button.prototype = Object.create(Nanocomponent.prototype)
// This the new API.
Button.prototype.initialState = function (done) {
var self = this
xhr('/foo/bar', function (err, data) {
self.state.data = data
done()
})
}
Button.prototype.createElement = function (color) {
this.color = color
return html`
<button style="background-color: ${color}">
Click Me
</button>
`
}
// Implement conditional rendering
Button.prototype.update = function (newColor) {
return newColor !== this.color
}
In Node, we could wait for all these functions to resolve, before doing a final render. It's a lil messy, but might be the best we got. Thanks & thoughts very welcome! ๐
Hello,
I'm wondering about how one would trigger a render from from inside a component mounted inside a choo app.
I'm referencing your leaflet component and the lifecycle diagram, and want to take a swing at a google maps one, but I'm having trouble figuring out how to emulate React's setState
. Choo is cool because you have absolute control about when to rerender, but I can't wrap my head around how that gels with the ability of a component to trigger it's own render based on setState
.
It seems that if I want render
to be called on my component, I would have to pass in the emit
fn? Is that the case, or is there a way I can force a render without any upstream dependencies?
Thanks for any help you could provide!
Am I missing something? If I change the choo example to have 2 buttons
var component = require('nanocomponent')
var html = require('choo/html')
var choo = require('choo')
// create new nanocomponent
var customButton = component({
render: function (data) {
return html`
<button>${data}</button>
`
}
})
var app = choo()
choo.router(['/', mainView])
document.body.appendChild(choo.start())
mainView (state, prev, send) {
return html`
<section>
${customButton(state.one)}
${customButton(state.two)}
</section>
`
}
it doesn't really cache those elements when state.one
and state.two
change, right?
I've made a nanocomponent, but this.element is undefined while server rendering, and it makes bankai stop when I try to do anything with it, in my case calling getElementsByClassName().
As you can see int the screeshot, it works fine on the browser. Any ideas? Am I doing something wrong, or could this be an issue?
(I'm using an example from Bootstrap as template)
This issue may apply more broadly to the way nanocomponent
manages component instances.
However, in the below example the resulting list contains one item, as opposed to the expected three:
const component = require('nanocomponent')
const html = require('bel')
const li = component({
render: function (item, index) {
return html`
<li>${item}</li>
`
}
})
const ol = component({
render: function (items) {
return html`
<ol>
${items.map(function (item, index) {
return li(item, index)
})}
</ol>
`
}
})
const state = {
items: ['one', 'two', 'three']
}
function mainView (state) {
const { items } = state
return html`
<section>
${ol(items)}
</section>
`
}
document.body.appendChild(mainView(state))
My hunch here is that the isMounted
check, is causing the issue.
I've experienced an interesting bug where I need to use beforerender
to make an xhr call to get some data, and change my component based on the data i get in xhr call, after the call is complete, i have been calling this.render()
to re-render the element with the new data. This is an example of my component:
PreviewComponent.prototype.createElement = function (state, emit) {
this.state = state
this.emit = emit
return html`
<div class="container">
<div id="editor-container" class="row">
${this.preview}
</div>
</div>
`
}
PreviewComponent.prototype.beforerender = function () {
// make the xhr request and get data back
this._getScript((data) => {
const html = document.createElement('div')
html.innerHTML = data.html
this.preview = html
// this bugs out!
this.render(this.state, this.emit)
})
}
I was trying to log see the component on create, and what would happen when i theoretically would unload the component. The component doesn't unload, but does come back with the element being empty. i.e.:
I am currently just hacking around it and instead of render just calling a set timeout, which helps with unmountinng the component for now; something like this:
setTimeout(function () {
// render
}, 0)
I would make a PR but I am still not sure how to fix it, if anyone knows how, lmk!
Thanks ^__^
The use of a placeholder
because it uses nanomorph
under the hood, means that the element that gets passed to the first argument f onupdate
is actually NOT the one that gets inserted into the document and returned by the first render. Thus, any attempt to modify that element has no impact on the actual DOM element.
The root cause is that due to using polite-element
the actual element that gets inserted is the placeholder element. Then the rendered element is used to diff and update the placeholder element. Thus the placeholder
DOM element is the one that needs to be returned and provided to the onupdate
element and any other place that the element is passed in.
Work around: Cache the element that gets passed into onload
(this seems to be the right one) and use this in onupdate
, or don't use a placeholder
element.
Here's a test case that shows the behaviour.
var component = require('nanocomponent');
var html = require('bel');
var assert = require('assert');
var el;
var comp = component({
onupdate: function (e, name) {
// This e refers to the element that was used by nanomorph in polite-component to diff
// and update the placeholder element
e.textContent = 'Updated, ' + name;
// This line will work and update the element:
// el.textContent = 'Updated, ' + name;
// This will throw, as the elements are not equal
assert.equal(e, el, 'Element should be equal');
},
placeholder: function (name) {
return html`
<div>Placeholder, ${name}</div>
`;
},
render: function render(name) {
return html`
<div>Hello, ${name}</div>
`
}
})
// Render "Placeholder, Mike" and then "Hello, Mike"
el = comp('Mike');
// Try to set the component to 'Updated, sarah' after 1 second
setTimeout(() => {
comp('Sarah');
}, 1000);
document.body.appendChild(el);
In order to mount in iframes, we need to dynamically figure out the correct document
https://github.com/choojs/nanocomponent/blob/master/index.js#L32
I have a simple component that should render an svg. But nanocomponent throws
Uncaught Error: nanocomponent: createElement should return a DOM node
at assert (index.js:21)
at Block.Nanocomponent._handleRender (index.js:93)
at Block.Nanocomponent.render (index.js:71)
at Object.values.map.block (blockPanel.js:33)
at Array.map (<anonymous>)
at BlockPanel.createElement (blockPanel.js:33)
at BlockPanel.Nanocomponent._handleRender (index.js:90)
at BlockPanel.Nanocomponent.render (index.js:71)
at Choo.mainView [as _handler] (index.js:21)
at Choo._prerender (index.js:244)
that's because this line fails. I think this assertion should be fixed to include svg (and actually everything that can be handled by bel
itself)
https://twitter.com/mattdesl/status/956940170468691968
It would be interested for comparisons sake to implement the example that @mattdesl does with preact in the above tweet.
Trying to use a component multiple times but it seems like the first rendered element is returned on subsequent calls. Is this intended?
index.js
var component = require('nanocomponent')
var html = require('bel')
var button = component({
render: function (text) {
return html`
<button>${text}</button>
`
}
})
button('one') == button('two') // => true
package.json
{
"dependencies": {
"bel": "^4.5.1",
"nanocomponent": "^2.0.0"
}
}
Judging by nanocomponent-adapters/react
my initial impression was that after calling a nanocomponent
s render function, the returned element
can be called to trigger component updates, and additionally even re-render the component.
However, in both my testing and in reviewing the source code here it seems as thought this isn't the case. In fact does this comment more accurately describes the overall intent?
So to clarify is it up to an author of nanocomponent
components to decide how to best manage updates/renders of a component eg. using nanomorph or morphdom or something else entirely via a components onupdate
handler? Also, should the above example in nanocomponent-adapters/react
be considered a POC and not reflective of the actual implementation or intent of the nanocomponent
API?
Here is the remaining work from the initial 6.0.0-0 beta PR:
this.element
is only briefly mentioned in docs, but is a pretty handy and key piece of the nanocomponent API. Would be good to document for users.
I was playing around with nanocomponent
and component-box
as possible target solution for jsx compilation and stumbled upon an issue.
Sometimes we may expect updaing a component state by calling render
, even when the component is not in the DOM, eg.
let menu = new Menu()
// these render calls do not incur update call
menu.render({ content: 'Home' })
menu.render({ content: ['A', 'B'] })
document.body.appendChild(menu.render())
Does it make any sense to invoke update
even for detached components? We can imagine some components not dealing with DOM at all.
// Implementer API
var Nanocomponent = require('nanocomponent')
var html = require('bel')
function MyButton () {
if (!(this instanceof MyButton)) return new MyButton()
this._color = null
Nanocomponent.call(this)
}
MyButton.prototype = Object.create(Nanocomponent.prototype)
MyButton.prototype._render = function (color) {
this._color = color
return html`
<button style="background-color: ${color}">
Click Me
</button>
`
}
MyButton.prototype._update = function (newColor) {
return newColor !== this._color
}
I might be going nuts here, but does the readme example actually work? On subsequent calls to render
, you would have to mutate this._element
with the new color right? See https://github.com/yoshuawuyts/nanocomponent/blob/master/index.js#L48
Since the render function only returns an element, it would only get captured on the first render()
call.
I'll double check this in a moment.
Not sure if we want this issue here or in nanomorph, but looks like proxy nodes are leaked when a component is morphed to a new location in the dom. For example, if these two views are morphed, a proxy node will be rendered rather than the component:
function viewA (state, emit) {
return html`
<body>
<a href="/">beep</a>
<a href="/boop">boop</a>
<div>
${component.render()}
</div>
</body>
`
}
function viewB (state, emit) {
return html`
<body>
<a href="/">beep</a>
<a href="/boop">boop</a>
${component.render()}
</body>
`
}
You can see this behavior here:
Also interesting to note if the component's update function returns true
, the component will leak the proxy node on first morph, while subsequent morphs will correctly return the component.
Hey there!
I am coming to the Choo universe (chooniverse?) from React and so far I like the lighter-weight approach. There were some confusing things about the Nanocomponent API that held me up a bit. I'm curious about the reasoning behind the API method names.
update()
called something like shouldUpdate()
? As a user, I would expect the component to re-render if I call this.update()
on
prefix? Is there a precedent for this? Same issue as above, where I would expect load()
and unload()
to do something different. Maybe it would make more sense to use mount
/ unmount
since those terms are used elsewhere in the Choo documentation?These questions may have simple answers but they weren't obvious to me.
Thanks for all your work!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.