choojs / nanocomponent

🚃 - create performant HTML components
https://leaflet.choo.io/
MIT License
366 stars 30 forks source link

Question about lifecycle, internal component state #50

Closed jonjaques closed 7 years ago

jonjaques commented 7 years ago

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!

bcomnes commented 7 years ago

This is a common question. We should add an example to the README.

If you are handling events internally and need to trigger re-renders, you can just call this.render(args), so the only thing to decide on is how you want to handle arguments. On solution is caching arguments e.g.

var Nanocomponent = require('nanocomponent')
var html = require('bel')
var SomeEventEmitterThing = require('foo')

class Component extends Nanocomponent {
  constructor () {
    super()
    this.arguments = []
    this.bus = new SomeEventEmitterThing()

    this.handleInternalUpdates = this.handleInternalUpdates.bind(this)
  }

  handleInternalUpdates (someNewArg) {
     // re-render with old arguments
     this.render.apply(this.arguments)
  }

  createElement (foo, bar, baz) {
    this.arguments = arguments
    return html`
      <div>${foo} ${bar} ${baz}</div>
    `
  }

  update (newColor) {
    return false
  }

  load {
     this.bus.on('event', this.handleInternalUpdates)
  }

  unload {
   // usually there is no point to keep running render on unmounted components
    this.bus.removeListener('event',  this.handleInternalUpdates)
  }

}
jondashkyle commented 7 years ago

Yeah—an example in the readme would be helpful, but also think it could be worth exploring ways of making it more default, rather than needing to manually store the previous props, etc…

jonjaques commented 7 years ago

Great, thanks! I did see the mention about caching internal args, but coming from React it scared me. That being said, I don't know enough about React internals and perhaps they do the same thing for purposes of comparing props. Will see about submitting a PR 😄

bcomnes commented 7 years ago

Agreed! One idea is that we auto-cache arguments and let you do something built in along the lines of this.bus.emit('render') and then the render function will get last args and whatever internal state you have changed. React does a lot of this stuff automatically, and one area this was exploring was not making these assumptions for people to cut down on API size and allow more freedom/creativity, but this is common enough to shortcut for people maybe.

jondashkyle commented 7 years ago

Definitely. I think having the render method and some form of internal state tracking while continuing to expose the internals, so if you want to manage it yourself nothing changes, could be a solid solution.

bcomnes commented 7 years ago

I think this idea might fit with https://github.com/choojs/nanocomponent/issues/44