简体   繁体   中英

How to not re-render the whole list in Mithril

I've used react for quite some time and wanted to try out Mithril.js.

Went through the documentation and the examples and liked what I saw, so I said I should get my hands dirty and start coding!

I have a smiple API call that receives a JSON data and then outputs ul list with all the items. I've integrated GSAP TweenMax for animations and what I'm trying to achieve is very simple - I fade everything in, on onload and then onclick I want to fade to fade an element out and remove it from DOM / data.

What seems to be happening is that the element is fading out, the whole ul list is being re-rendered and that element remains in the DOM with 0 opacity:

var Item = {
    list: function() {
        return m.request({method: 'GET', url: '/api/items'});
    }
}

var dm = {
    controller: function(data) {                
        var items = Item.list();
        return {
            items: items,
            remove: function(item) {                
                items().data.splice(items().data.indexOf(item), 1);
            }
        }
    },

    view: function(ctrl) {      
        return m('ul', [
            ctrl.items().data.map(function(item, id){
                return m('li',{
                    key: id,
                    config: fadesIn,
                    onclick: fadeOut(ctrl.remove.bind(this, item))
                }, item.title);
            })
        ]);
    }
}

var fadesIn = function(element){
    var tl = new TimelineMax();
    tl.from(element, .5, {opacity: 0});
}

var fadeOut = function(callback) {
    return function(e) {
        m.redraw.strategy('none');
        TweenMax.to(e.target, .5, {opacity: 0, onComplete: function() {
            m.startComputation();           
            callback();         
            m.endComputation();
        }});
    }
}

m.mount(document.getElementById('test'), dm);

I'm very new.. started reading up just yesterday.

Getting animation libraries to work with Mithril can be tricky. When libraries manipulate DOM state the synchronization with Mithril state can be broken.

Fortunately that wasn't the case: What you're missing is the isInitialized parameter for the config function, which is false only on the first call. Testing for that makes the fade-in only happen once:

var fadesIn = function(element, isInit){
    if(isInit) return;
    var tl = new TimelineMax();
    tl.from(element, .5, {opacity: 0});
}

In this simple example the redrawing can also be simplified, I've made a fiddle with a working example:

http://jsfiddle.net/ciscoheat/dkyc0ryc/

Since there are no DOM manipulations a call to m.redraw is enough to remove the div from DOM, but you're probably right in using start/endComputation when things get more complex. I would even move m.startComputation above the TweenMax.to call to make it extra safe, but if many other things are happening at the same time, that may block other redraws. You have to find a balance. :)

The call to m.redraw.strategy isn't needed in any case, I think. It's mostly used when you want nothing at all to happen (synchronously as well), but an async animation is starting so it won't have any effect.

Edit: Found another problem, the key cannot be set to the index of the map function, then it will change when an item is removed, messing up the redraw. I've updated the fiddle to use item.title as key instead.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM