Swizec Teller - a geek with a hatswizec.com

    You don't want to build your own list virtualization

    Chatroom is slow? I know, I'll just use list virtualization!

    Well now you have two problems.

    https://twitter.com/Swizec/status/958840988495093760

    List virtualization is hard, dear reader. So hard. “Pfft, I can build that in an hour,” I thought until I tried.

    It's been 3 days. Last night, I dreamed scrollbars and mouse wheel events.

    Hold up, what's list virtualization?

    List virtualization is a performance improvement technique for large lists or tables. Whenever your UI becomes slow because you're rendering too much stuff, you can use list virtualization to make it fast again.

    We've been hit with this problem recently at Yup. When our sessions get long, like hundreds of messages between tutor and student, tutors start complaining about the UI feeling sluggish.

    They can't type, they can't talk, it's like teaching through a paper bag. Terrible.

    We don't know why it became such a big problem right now, but 600 milliseconds to add a message to a chatroom is just too much. Needs to be fixed.

    That's where list virtualization comes in.

    Instead of keeping the whole hundreds-of-messages chatroom rendered, you render just 30 messages. Or 40, or 10. Whatever the number, you render a small subset of the messages.

    And that gives you a performance boost. Fewer DOM nodes to deal with, faster rendering times. Especially on slow computers.

    The problem may have become a problem because of the Spectre and Meltdown fixes that hit Intel processors with up to 30% slowdowns. When you're already using a slow $300 computer… yeah.

    How to virtualize a list, in theory

    So, how do you virtualize a list?

    Virtualizing a list is simple in theory. You maintain a window and move it around your list.

    A windowSize variable tells you how many nodes you're rendering, and a windowIndex variable tells you where to start rendering. Then you .splice your data array and render away.

    https://twitter.com/Swizec/status/958497773749395456

    I was doing it in Backbone, so it seemed super tricky. We had complex logic in place to append and remove and prepend messages to the list as necessary.

    Think things like

    1. Find previous message with $("nth-child")
    2. Manipulate with $(ul).append
    3. Find the correct scroll position

    Messy.

    After a few hours of that approach, I gave up.

    Screw DOM manipulation, it's 2018 and the DOM is fast. Re-render it.

    To my surprise, throwing away the list and re-rendering for every message insertion or scroll event works friggin’ great. Like really seriously great. Even in Backbone where you get none of React's diffing magic.

    I didn't run proper benchmarks, but on my 2017 MBP at home, re-rendering a 30-element list happens in 10 to 15 milliseconds.

    1. $("ul").html("")
    2. elements.splice(windowIndex, windowSize)
    3. loop and append

    That re-renders in just 10 milliseconds on my 15" MBP, 30 milliseconds on my 13" MBP. Both running RoR and sidekiq and webpack and the rest in the background.

    13" is so much slower because it's just dual core. I'll run a proper benchmark for this soon because I'm curious.

    My point is that re-rendering is fast and easy. You can totally get away with re-rendering on every mousewheel event if your windowSize is small enough.

    I now had a virtualized list. Worked great.

    https://twitter.com/Swizec/status/958518929504088064

    Except the UX is confusing. You get to the edge of the scrollbar and it just keeps going and going. Wat?

    Where things get tricky

    This strange scrolling UX is where things go belly up and life gets hard. Users don't expect to hit the edge of the scrollbar before the edge of the content, you see.

    So I tried a couple things and haven't really figured this out. I think I'm on the right path, but I'm also starting to lose my mind.

    Watch this gif. Scrolling up works great, but then you start scrolling down and everything goes topsy turvy.

    https://twitter.com/Swizec/status/958840988495093760

    The version in this gif tries to compensate for a few things.

    1. Uses an offset from the edge to start adding messages before you hit the edge
    2. Adjusts this offset to your scrolling speed
    3. Adjusts windowIndex delta to your scrolling speed

    Adding messages is basically just moving the windowIndex. That part is easy.

    But the mousewheel is a tricky beast. On my mouse, the smallest deltaY is 4 pixels. That's okay.

    The biggest deltaY I've seen is around 3000 pixels. In a single event call.

    So that's what I've been dealing with. I wish I could just use react-virtualized, but I can't because "We'll have time to rewrite later". Can't come soon enough 😅

    Update: OMG I DID IT

    It's not as smooth as Slack, but it's good enough for now.

    https://twitter.com/Swizec/status/959151765185691650?ref_src=twsrc%5Etfw

    Wasn't that complicated after all. Just a bunch of maths with magic numbers.

    Did you enjoy this article?

    Published on February 1st, 2018 in Frontend Web, JavaScript, Technical,

    Learned something new?
    Read more Software Engineering Lessons from Production

    I write articles with real insight into the career and skills of a modern software engineer. "Raw and honest from the heart!" as one reader described them. Fueled by lessons learned over 20 years of building production code for side-projects, small businesses, and hyper growth startups. Both successful and not.

    Subscribe below 👇

    Software Engineering Lessons from Production

    Join Swizec's Newsletter and get insightful emails 💌 on mindsets, tactics, and technical skills for your career. Real lessons from building production software. No bullshit.

    "Man, love your simple writing! Yours is the only newsletter I open and only blog that I give a fuck to read & scroll till the end. And wow always take away lessons with me. Inspiring! And very relatable. 👌"

    ~ Ashish Kumar

    Join 15,883+ engineers learning lessons from my "raw and honest from the heart" emails.

    ⭐️⭐️⭐️⭐️✨
    4.5 stars average rating

    Have a burning question that you think I can answer? Hit me up on twitter and I'll do my best.

    Who am I and who do I help? I'm Swizec Teller and I turn coders into engineers with "Raw and honest from the heart!" writing. No bullshit. Real insights into the career and skills of a modern software engineer.

    Want to become a true senior engineer? Take ownership, have autonomy, and be a force multiplier on your team. The Senior Engineer Mindset ebook can help 👉 swizec.com/senior-mindset. These are the shifts in mindset that unlocked my career.

    Curious about Serverless and the modern backend? Check out Serverless Handbook, for frontend engineers 👉 ServerlessHandbook.dev

    Want to Stop copy pasting D3 examples and create data visualizations of your own? Learn how to build scalable dataviz React components your whole team can understand with React for Data Visualization

    Want to get my best emails on JavaScript, React, Serverless, Fullstack Web, or Indie Hacking? Check out swizec.com/collections

    Want to brush up on modern JavaScript syntax? Check out my interactive cheatsheet: es6cheatsheet.com

    Did someone amazing share this letter with you? Wonderful! You can sign up for my weekly letters for software engineers on their path to greatness, here: swizec.com/blog

    Want to brush up on your modern JavaScript syntax? Check out my interactive cheatsheet: es6cheatsheet.com

    By the way, just in case no one has told you it yet today: I love and appreciate you for who you are ❤️

    Created by Swizec with ❤️