choojs / nanocomponent

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

How to manipulate choo state with nanocomponent method #45

Closed YerkoPalma closed 7 years ago

YerkoPalma commented 7 years ago

Hi :wave: 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.

bcomnes commented 7 years ago

The ides is state down, actions up. Fundamentally state gets passed down from the 'top' and is used and shaped as it traverses down your views/components. You use 'emit' to pass state updates up from the bottom. At any point in your view tree, you can change the shape of what you pass along, for example only passing the state that owned components need to render. Generally its not a good idea to modify app level state in views and you should be using the emitter bus to pass around events with values and do it all in one place. Its totally okay to re-shape state for the needs of owned components.

bcomnes commented 7 years ago

Here is an example: https://github.com/hypermodules/hyperamp/blob/master/renderer/elements/player/controls.js

YerkoPalma commented 7 years ago

So, the key should be to pass the choo emitter object to the component and keep manipulating state through events?

YerkoPalma commented 7 years ago

I've tried passing the emitter.emit function to my nanocomponent and it works :D But I encountered something weird, I'm stuck in an endless loop when firing events from nanocomponent. With this code

// component
function Navlink (opt) {
  if (!(this instanceof Navlink)) return new Navlink(opt)
  this.direction = opt  
  this.current = null
  this._emit = null

  this.navigate = this.navigate.bind(this)

  Nanocomponent.call(this)
}
Navlink.prototype = Object.create(Nanocomponent.prototype)

Navlink.prototype.navigate = function (e) {
  e.preventDefault()
  this._emit('navigate', this.current + 1)
}

Navlink.prototype.createElement = function (state, emit) {
  this._emit = emit
  this.current = state.current

  return html`
    <a href="#" onclick=${this.navigate}>
      <span class="icon-${this.direction}-open"></span>
    </a>
  `
}

Navlink.prototype.update = function (state, emit) {
  return true
}

// app
var app = choo()
app.use(function (state, emitter) {  

  state.current = 1

  emitter.on('navigate', function (year) {
    // if (year && state.current !== year) {
      state.current = year
      emitter.emit('log:info', 'Going to ' + year)
      emitter.emit(state.events.PUSHSTATE, '/?year=' + year)
    // }
  })
})

When I click in my component link, What is inside the 'navigate' handler get fired for ever until the browser throws an exception, only when I uncomment the condition in my handler it works, because of those intinite calls, only the first one has the correct data, the others have undefined as state. Do I have to check my state in every event handler? Or there is something I'm missing?

ungoldman commented 7 years ago

@YerkoPalma choo@6 has its own navigate event that fires whenever a route changes. If you name your event something else that might solve your problem. Please let me know if that helps!

https://github.com/choojs/choo/blob/master/CHANGELOG.md#600-same-as-it-ever-was

Basically your navigate event is causing a pushstate event, which is causing an internal navigate event, which is causing a pushstate event, which is causing a navigate event... etc

(note: edited the above a little bit when I read your example a bit more)

ungoldman commented 7 years ago

@bcomnes might be a good idea to namespace the internal choo events, like with a __navigate or choo-navigate or something, so it's less likely there will be namespace collisions like this.

YerkoPalma commented 7 years ago

Oh! I see, will check it asap. Thanks @ungoldman and @bcomnes for your time :)

ungoldman commented 7 years ago

@bcomnes also https://github.com/choojs/choo/blob/master/CHANGELOG.md looks like it hasn't been updated since june, is choo@6 officially out?

ungoldman commented 7 years ago

No problem @YerkoPalma! I think you found something worth rethinking in terms of choo's internal architecture 👍

YerkoPalma commented 7 years ago

Well, I think that my doubts/issues about Nanocomponent are resolved now, so I'm closing this. I'm also opening an issue in choo to discuss about namespacing events.

Choo issue here: https://github.com/choojs/choo/issues/547