DEB-11363 grid optimizations

@sergiopereiraTT Could you please review?
@slikrik98 I think you'll be interested in this change as well.

I profiled the performance of adding and removing rows and found the biggest bottlenecks to be DOM reads (`scrollTop` inside `updateRowCount`) and writes (`width` inside `updateCanvasWidth`).  This change refactors out the read of `scrollTop` and the write to `width` where possible.

Inside `updateRowCount`, there is a debounced invokation of `render` using the `h_render` variable.  So after this change `updateRowCount` shouldn't be the source many problems for us.

I do see other places in our code where `render` is being called (directly or indirectly) too frequently.  `render` is expensive as it rebuilds the grid, as @slikrik98 has pointed out.  This change doesn't tackle that part of the problem.

My understanding of what's going on here is that `scrollTop` is being read to preserve the scroll position as the height of the canvas changes.  I noticed in my tests that reading `scrollTop` before and after the height change can generate a different value for `scrollTop`.  However this only occurs when you are scrolled to the bottom of the grid and you remove a row.

So after the height changes, there is a chance that `scrollTop` is out-of-date.  There is an `if` statement that handles this.

In the first case, `scrollTop` is `0`.  We don't need to update `scrollTop` because if `scrollTop` was `0` before the height change, we are not running into our corner case and it will be `0` after the height change.

The other two cases ("maintain virtual position" and "scroll to bottom") both call `scrollTo`.  Since `previousScrollTop` will be out of the range of the viewport, `scrollTo` will always update `scrollTop`, and the cached value will no longer be out of date.

What I could do is make handling of this corner case more explicit in the code.  I'll add a commit that does this.