Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to avoid rerendering a certain component? #207

Closed
kwoon opened this issue Aug 1, 2016 · 19 comments
Closed

How to avoid rerendering a certain component? #207

kwoon opened this issue Aug 1, 2016 · 19 comments

Comments

@kwoon
Copy link

kwoon commented Aug 1, 2016

I have a page on which a lot of widgets. All widgets frequently polls the server. And one of these widgets is the timer that simply shows the time and it has no any business logic. Timer ticks every second and every second WHOLE view is rerendered! But I want to make this a timer ticking, and only changed his view. Is there any do it or I in all cases is to use the state?

@MattMcFarland
Copy link
Contributor

Can you try using a separate model for the timer widget with its own namespace? Maybe that could fix it? I think the name of this issue should be renamed though to "whole page is rendering when only a few items changed" or something like that. Also it might be helpful if we can see your code so we can view the implementation.

@kwoon
Copy link
Author

kwoon commented Aug 3, 2016

@MattMcFarland I originally did so, but every view in my project updated with every tick.

Here example of my model

module.exports = {
  namespace: 'timer',
  state: {
    time: new Date()
  },
  reducers: {
    setTime: (data, state) => ({ time: data })
  },
  effects: {
    updateTime: (data, state, send, done) => {
      send('timer:setTime', data, done)
    }
  },
  subscriptions: [
    (send, done) => {
      setInterval(() => {
        send('timer:updateTime', new Date(), done)
      }, 1000)
    }
  ]
}

@yoshuawuyts
Copy link
Member

@kwoon ah yeah, that's what happens with virtual-dom renders; to change a single element the whole page is re-computed and the minimal amount of changes are performed on the actual DOM.

A way to improve the rendering speed is to use thunking for individual components on the page. Given the same input, the same output is returned. The vdom-thunk package should be helpful for that.

Does this answer your question?

@MattMcFarland
Copy link
Contributor

Ahhh, this must be what caused codemirror to lose focus.

@kwoon
Copy link
Author

kwoon commented Aug 3, 2016

@yoshuawuyts Thanks! It is even better than i expected! Just like shouldComponentUpdate in React.

@YerkoPalma
Copy link
Member

@yoshuawuyts wait, but choo use yo-yo which uses morphdom which is not virtual dom right? So the view should update properly if yo-yo#update is used? or I'm confused ❓

@kwoon
Copy link
Author

kwoon commented Aug 3, 2016

@YerkoPalma I think this is not the best name for the package. The functionality of this package is much wider.

@kwoon kwoon closed this as completed Aug 3, 2016
@YerkoPalma
Copy link
Member

@kwoon what package?

@kwoon
Copy link
Author

kwoon commented Aug 3, 2016

@YerkoPalma vdom-thunk

@MattMcFarland
Copy link
Contributor

@YerkoPalma I agree, I'm not sure how vdom-thunk is going to work with morphdom?

@MattMcFarland
Copy link
Contributor

@kwoon can you share an example when you get it working?

@yoshuawuyts
Copy link
Member

related issue choojs/nanohtml#40

@YerkoPalma
Copy link
Member

Well I understand the thing about the thunk function, My only doubt is why does a vdom library fits with a non vdom library?

@YerkoPalma I think this is not the best name for the package. The functionality of this package is much wider

Is that why? the vdom thunk is also compatible with native dom?

@yoshuawuyts
Copy link
Member

So yeah, the vdom-thunk package is compatible with bel components - from the source all code is pretty much only about copying function parameters.

The implementation does feel a bit too complicated for my taste though; it does some stuff with objects and keys that feels off. For bel components we could def create a simpler version of this package

@YerkoPalma
Copy link
Member

Ok, I'm starting to understand 😄 but from the related issue there is this comment

Initially it feels like this should be in the diffing library or yo-yo as bel doesn't know about previous elements.

I agree with that, I feel like choo should enforce or recommend the use of the yoyo update function, and update that function if necesary, I don't know why the thunk function should be in bel library

@yoshuawuyts
Copy link
Member

yoshuawuyts commented Aug 4, 2016

I don't know why the thunk function should be in bel library

I feel the goal of bel is to create standalone components that work anywhere.
Like onload and onunload provide hooks into any DOM abstraction, thunk
would provide a way to optimize those components.

The way I view a typical element in bel would be along these lines:

const thunk = require('bel/thunk')
const assert = require('assert')
const html = require('bel')

module.exports = thunk(createMyButton)

// create a button that has a color, a value inside it
// and does a thing when clicked
// (str, str, fn(clickEvent)) -> str
function createMyButton (opts, onclick) {
  opts = opts || {}

  assert.equal(typeof opts, 'object')
  assert.equal(typeof onclick, 'function')

  const color = opts.color || 'blue'
  const name = opts.name || 'click'

  return html`
    <button onclick=${onclick} style="background-color: ${color}">
      ${name}
    </button>
  `
}

Using this component would be the same for pretty much any framework, but now
it's optimized for re-rendering / vdom trees. Using it in choo would look
like this:

const html = require('choo/html')
const Button = require('../elements/my-button')

// model bound to this particular view
const model = {
  namespace: 'button',
  state: {
    color: purple,
    name: 'click me!'
  },
  effects: {
    hey: () => console.log('hey!')
  }
}

module.exports = sectionView
sectionView.model = model

// A button with a button in it
// (obj, obj, fn) -> str
function sectionView (state, prev, send) {
  const button = Button(state.button, (e) => send('section:hey'))

  return html`
    <section>
      <h1>Hello y'all</h1>
      ${button}
    </section>
  `
}

I feel like thunk is an integral part to creating components in such a way, I
can't think of a time where it wouldn't make sense to thunk an element. Does
this clarify it a little?

@YerkoPalma
Copy link
Member

Pretty clear now, thanks. :)

@MattMcFarland
Copy link
Contributor

Man I had to make sure codemirror was fully rendered first and that was not easy to figure out.

https://github.com/trainyard/choo-codemirror

would be nice if there was a way to completely ignore parts of the dom. like special portals or something.

@yoshuawuyts
Copy link
Member

Oops, found a bug in my code. In order to prevent multiple instances of the same element conflicting on the page, the export should be wrapped in a function. E.g.

// oops
module.exports = thunk(createMyButton)
// phewwww
module.exports = function () {
  return thunk(createMyButton)
}

would be nice if there was a way to completely ignore parts of the dom. like special portals or something.

@MattMcFarland not sure that's possible in a DOM-compliant way using the current version of morphdom :/ - maybe if we enumerate the issues we're currently facing with creating new components we might be able to come up with a better way to do this

@kwoon kwoon changed the title Timer How to avoid rerendering a certain component? Aug 7, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants