I've got a React Virtualized InfiniteList/Grid combination, following the basic pattern from the documentation (https://github.com/bvaughn/react-virtualized/blob/master/docs/InfiniteLoader.md#infiniteloader-and-grid).
It works great, except for two things:
If I scroll too fast, instead of waiting for things to catch up, it resets the scroll to the top of the grid
The combo only fetches the first N results (eg. 125) ... even though I have 859 results (and even though I have provided 859 as the rowCount prop)
This is especially strange because it fetches in increments of 25, so this means that everything works fine the first 5 times and then inexplicably fails on the 6th.
I've tried everything I can to figure out what's going on, and even when I fill the React Virtualized code with console.log and debugger statements I still can't figure out why it stops at 125 results (or why fast scrolls reset).
Can anyone more familiar with React Virtualized point me to the spot in the code where it decides whether to keep fetching or stop (and possibly reset the start index to 0)? I can tell that the InfiniteScroll's onRowsRendered and scanForUnloadedRanges and Grid'sonSectionRendered are involved, but I still can't figure out where the value processing stops and the actual "decide whether to keep going" logic begins.
As #brianvaughn's suggested in his comment, the InfinititeLoader component (https://github.com/bvaughn/react-virtualized/blob/master/source/InfiniteLoader/InfiniteLoader.js) is partly responsible. Whenever rows are rendered _loadUnloadedRanges method gets called from scanForUnloadedRanges, and it handles loading the ranges that scanForUnloadedRanges found.
However, that's only part of the equation. The parameters that are passed in to onRowsRendered come from Grid. The Grid _updateScrollTopForScrollToRow method gets called as the user scrolls, and that method has the following line:
const targetIndex = Math.max(0, Math.min(rowCount - 1, scrollToRow))
That targetIndex will ultimately affect which rows get loaded, and as you can see it depends on the rowCount that is passed in initially.
This is what caused problems for me. I started with an InfiniteLoader + List combination, and in that setup a "row" means the same thing to both components. However I then switched to a InfiniteLoader + Grid combination, and to a Grid a "row" is an array of InfiniteLoader rows. In other words, a Grid "row" is a row of cells, but an InfiniteLoader "row" is a cell.
My method for calculating the rowCount was changed to use the Grid definition of a "row", but since it was being passed to InfinniteLoader I should have used InfiniteLoader's definition of a "row" instead. In other words, I should have returned just numItems, not numItems/columnCount.
All this could have easily been avoided if the React Virtualized used an "items" prop for InfiniteLoader instead of using a name that's also used (with two different meanings) by two other React Virtualized components ... but at least now I have a much better understanding of how these components work.
Related
REF: demo at https://codesandbox.io/s/1vy7ljo877
The demo uses 2 approaches to render some images.
The first row renders an element inside CellMeasurer
while the second row renders a function with signature: ({ measure }) => )
I find that for the function render, while the size is calculated correctly, the render method gets called n^2 # of times where n is approximately the number of items that it tries to display.
If deferredMeasurementCache is turned on, the problem gets exponentially worse since n becomes the size of the collection.
The above results in crazy long page load times and very sluggish behavior when scrolling.
Is this normal behavior or am I implementing the function inside CellMeasurer incorrectly?
If deferredMeasurementCache is turned on, the problem gets exponentially worse since n becomes the size of the collection.
If you're not passing the CellMeasurerCache to Grid as a deferredMeasurementCache prop, then Grid isn't guaranteed to work correctly. It's necessary for it to know about the cache for a couple of cases. (Maybe check out this part of the docs to make sure you aren't misunderstanding how CellMeasurer is supposed to work when used to calculate the width/height of a row/column.)
That being said, what you're describing sounds like a bug, but I'm not sure it actually is. At a high level, it's caused by the CellMeasurer measure method calling Grid.recomputeGridSize() which synchronously triggers forceUpdate() (causing all visible cells to re-render).
At first I thought the solution might be to just debounce or queue a setState action instead. Unfortunately that only pushes the problem away a little; the possibility still exists. I can't debounce for too long, or I risk the Grid showing cells to the user that are clearly the wrong size. (A debounce that's too big might even allow a user to keep scrolling ahead of it, outrunning the re-render.) It's also possible that images will load far apart (after a delay) in a real app, defeating any debounce I might do anyway.
The problem here is that any time a cell reports it has a different size, the Grid needs to relayout the other cells (potentially shifting them up or down). In this simple example it seems unnecessary- because all images end up having a uniform width and height. This isn't typical though. Since you've configured your CellMeasurerCache to measure height, and since the height of each column impacts the height of the row (and thus all other columns, as explained in the docs I linked to above), Grid has to re-render all cells each time one reports a change.
Put another way, if you added more rows, and changed your example to be more like this:
const makeImages = (count=10, startIndex=0) => {
const width = 100;
const imagesArray = _.times(count, (index) => {
const height = 100 + Math.round(Math.random() * 200);
return {
key: startIndex+index,
src: `http://placehold.it/${width}x${height}/${((1<<24)*Math.random()|0).toString(16)}/fff?text=${startIndex+index}`,
}
});
return imagesArray;
};
Then maybe it's easier to see why a change to a cell in row 1, column 1 could potential impact the height of all cells in row 1, and the position of all cells in the grid.
Ok I am a complete noob to pygame and programming in general. However I have been working on a game where the player falls and dodges objects. The idea is that the player remains within the top 1/4 of the screen and everything scrolls upwards to give the illusion of moving. This works fine however the code below is where I try and get another wall to spawn. This should work by deleting the sprite when it goes off the screen and spawning a new sprite when the image starts to go off the screen. The -2 is how far it should cross until another is spawned and my logic was to check the size of the group. While it only contained one it would iterate through the next section of code defining a new wall sprite identical to the old one but positioned below it. However this causes the program to crash probably because the old sprite is deleted causing an infinite loop. So if this is true how do you define a new sprite with the same attribute whilst not killing the other?
Also I realised that the code wont even work multiple times because I used "left_wall" which wont be defined after its been deleted so I tried using "wall" as I iterate through the group defining each sprite as wall in the for loop so every time a new wall is added it should be defined as wall so that I works infinitely. however I this error message AttributeError: 'Game' object has no attribute 'wall'. So at this moment I am stuck, not really knowing what to do next.
edit: I forgot to take off "self" when using "wall" which was the cause of the attribute error however it is still crashing like I mentioned in the first paragraph which is probably due to the old sprite being deleted.
sorry if this is confusing or not a properly structured question (like I said, I am a noob).
for wall in self.left_wall_sprite:
wall.rect.y -= self.player.vel.y
if wall.rect.bottom < 0: # move left wall
wall.kill()
if wall.rect.top == -2: # a new wall needs to be spawned
while len(self.left_wall_sprite) < 2:
self.new_left_wall = self.left_wall
self.new_left_wall.rect.top = self.left_wall.rect.bottom
self.all_sprites.add(self.new_left_wall)
self.left_wall_sprite.add(self.new_left_wall) # add the new sprite to the wall group so that it runs through the same cycle
With only this part of the code, it is hard to tell for sure what is going on - but what can be detected is that you create a new wall section, but do not "leave things as you found them" - so, that the code will still try to move the old wall.
If you really need to create a new sprite, go all the way: once the condition is met for the change:
get a reference to the old wall
remove the old wall from all groups (the .kill method)
create a new wall (create a new instance - you are just making an
assigment to an existing instance).
insert the new instance in all relevant groups.
As the code is, you are just pointing to the same wall object (self.left_wall) so that "self.let_wall" and "self.new_let_wall" are just references to the samething, and when you change a parameter (like rect.top) in one side, you can see the same change in the other.
Without the code for the class of your wall objects, one can't now if everything is set for correctly creating a new instance, but that could typically be done by self.left_wall = Wall((wall.rect.bottom, wall.rect.left, wall.rect.width, wall.rect.height)) (if your class's __init__ will take a rect as first parameter). This replaces the old self.rect_wall (which should have a reference on the wall variable yet) - you then just add it to the new groups.
But it looks like you can just reuse the same sprite - so, jsut update the top coordinate as you are doing -no need to fiddle with group containment or anythng:
if wall.rect.bottom <= 0:
wall.rect.top = wall.rect.bottom # or "0" at once
I was wondering if there is any way to accelerate/speed up the UItableView/[reload data].
Even though I reload the data on the main thread using:
> [self.tableview performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:No]
I still think the results is quite slow (2 to 3 seconds to refresh each time the table is being reloaded, making a bad user experience if the user keep refreshing the table).
For a bit of background here is how it works at the moment.
1) I create my UITableView from data coming from the database on apple starting (until here no problem and I am fine if its taking few seconds).
2) I click on one of the item of the table view, which opens a new screen to modify the elements.
3) Once the elements is modified, I change the database of my table view so that the elements I changed comes at the top of my table view.
4) When I press the back button on the screen to change my elements, I implements the ViewWillAppear of my UITableView and I reload the data on the main thread.
5) What I observer is a latency of about 2/3 seconds to go from one screen to another. Removing the reload data line from my code, make the transition instantaneous. So the problem is really in the reload data.
Each row of my UITableView has the following elements inside:
A background color coming from a file (this is basically a gradient, the file is about 20Ko, it is a PNG, the file is store on the phone)
1 or 2 UIImageView (depending on the status of what I display) (about 5Ko each, very small PNG icons, the file is store on the phone)
3 UILabel
1 UIButton (inside this PNG, there is a picture, the original size of the picture may vary from 200 to 300ko, the original source is coming from internet. I use SDWebImage to load it/cache it)
I would think I may have too much information on each of my cell, as I can see a big difference in loading time when I only have one row, versus having 10rows.
Would like to have some input from the community to see if anything can be done to improve. The best would be to be able to update the UI in the background, so for example, when I am modifying my elements, I do reloaddata in the background, so that when I come back to the previous screen that shows the UITableView my view is already refreshed,
Thanks
Ok I foudn the solution on this website:
http://www.raywenderlich.com/31166/25-ios-app-performance-tips-tricks#drawing
20) Optimize Your Table Views
Table views need to scroll quickly — when they don’t, users really notice the lag.
To keep your table views scrolling smoothly, ensure that you’ve implemented all of the suggestions below:
Reuse cells by setting the correct reuseIdentifier.
Make as many views opaque as possible, including the cell itself.
Avoid gradients, image scaling, and offscreen drawing.
Cache the height of any rows if they aren’t always the same.
If the cell shows content that comes from the web, be sure to make those calls asynchronously and cache the responses. (Personnal note on this one as it was my issue: look at this other website http://keighl.com/post/sdwebimage-uitableviewcell/ that explains how to use SDWebImage in a UITableView. Very useful.
Use shadowPath to set up shadows.
Reduce the number of subviews.
Do as little work as possible in cellForRowAtIndexPath:. If you need to do some work, do it only once and cache the results.
Use the appropriate data structure to hold the information you need. Different structures have different costs for different operations.
Use rowHeight, sectionFooterHeight and sectionHeaderHeight to set constant heights instead of asking the delegate.
You say "A background color coming from a file" - this alerts me that you're doing IO work, which is generally time-consuming, I'm curious if you remmed out the loading a colour from a file if you get a significant speed increase?
I'm developing a custom table component for very large and very custom content. Therefore I decided not to go with TableView, but start from scratch (i.e. Control). I adopted the idea of the VirtualFlow to create and manage only the visible part of the table and reuse cells that have become invisible. However I needed virtual rows and columns.
My CustomVirtualFlow implements layoutChildren(). From the component's size and scrollbar positions I know which cells are visible. If necessary, I add cells to the VirtualFlow. I then update the content and css pseudo class states (selected, focused, ...).
This works almost fine ... currently, my only problem is that the css styles are sometimes lagging: newly created cells are shown with e.g. wrong backgrounds for a moment and then get correcteted with the next pulse.
My explanation for this is that JavaFX performs layout in two passes:
first a css pass and secondly the layout pass that calls layoutChildren().
The css of newly added children (during layoutChildren) is therefore not processes correctly.
I tried to call applyCss() directly, which helps, but seems to do too much because it takes a lot of time.
My question is:
How is the correct way to add nodes during layout, i.e. if the size of the component makes it neccessary to use further nodes?
If it is not during layoutChildren(), where else should I do it?
Is there a way to get a callback when a View appears/disappears on the screen?
I need this for analytics purposes - eg. I need to check how many times people saw a view versus how many times they clicked on it.
I can tell whether a view is on screen at a certain point in time but I'm wondering whether there is a callback for that.
Also note that I'm not interested when a View's state becomes VISIBLE and not even when a view gets rendered (because eg. in a ViewPager the Views of the second page are rendered before they actually appear on screen).
We did several workarounds for certain scenarios but I'd like to have something which is more generic. We need it in ListViews, GridViews, ScrollViews and ViewPagers and each one of them presents different challenges.
Does ViewTreeObserver.OnGlobalLayoutListener help? I have never used it myself though.
See: http://developer.android.com/reference/android/view/ViewTreeObserver.OnGlobalLayoutListener.html