ReactJS:建模双向无限滚动 [英] ReactJS: Modeling Bi-Directional Infinite Scrolling

查看:233
本文介绍了ReactJS:建模双向无限滚动的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们的应用程序使用无限滚动浏览大型异构项目列表。有一些皱纹:




  • 我们的用户通常拥有10,000个项目的列表,并需要滚动浏览3k +。
  • >
  • 这些都是丰富的项目,所以在浏览器性能变得不可接受之前,我们只能在DOM中有几百个。

  • 项目高度不一。 li>
  • 这些项目可能包含图像,我们允许用户跳转到特定的日期。这很棘手,因为用户可以跳转到列表中需要在视口上方加载图像的点,这会在加载内容时将内容向下推。未能处理这意味着用户可能跳转到日期,但会被转移到更早的日期。 已知的,不完整的解决方案:





    我并没有寻找完整解决方案的代码(尽管那会很棒)。相反,我正在寻找React方式来模拟这个情况。滚动位置状态与否?我应该跟踪哪些状态以保留我在列表中的位置?我需要保持什么状态,以便在滚动时显示底部或顶部时触发新的渲染? 解决方案

p>这是无限表和无限滚动情景的混合。我发现的最佳抽象如下:

概述

制作一个< List> 组件,它接收所有子元素的数组。由于我们不渲染它们,分配它们并丢弃它们真的很便宜。如果10k分配太大,您可以改为传递一个函数来获取范围并返回元素。

 < List> 
{thousandelements.map(function(){return< Element />})}
< / List>

您的 List 组件正在跟踪滚动位置并且仅渲染正在看到的儿童。它在开始时添加了一个大的空div来伪造之前没有渲染的项目。



现在有趣的部分是,一旦元素组件被渲染后,您测量其高度并将其存储在 List 中。这可以让您计算垫片的高度,并知道应该显示多少元素。



图片



你在说,当图像加载时,它们会使所有的东西跳下来。解决方法是在img标签中设置图片尺寸:< img src =...width =100height =58/> 。通过这种方式,浏览器不必等待下载它才能知道它将显示的大小。这需要一些基础设施,但它是真的值得。



如果您事先不知道大小,然后添加 onload 听众对你的图像,当它被加载,然后测量其显示的尺寸和更新存储的行高度,并补偿滚动位置。



随机跳元素



如果您需要跳到列表中的随机元素,那么需要使用滚动位置的一些技巧,因为您不知道中间的元素。我建议你做的是平均你已经计算的元素高度,并跳转到最后已知高度的滚动位置+(元素数量*平均值)。

由于这不是确切的,当你回到最后一个已知的好位置时会引发问题。发生冲突时,只需更改滚动位置即可修复它。这将会移动滚动条,但不应该太多影响他/她。



反应细节



您想提供渲染所有元素,以便在渲染时保持它们。有两种策略:(1)只有n个键(0,1,2,... n),其中n是可以显示的元素的最大数量,并使用它们的位置模n。 (2)每个元素有不同的键。如果所有元素共享相似的结构,则可以使用(1)重用其DOM节点。如果他们不使用(2)。

我只有两个React状态:第一个元素的索引和正在显示的元素的数量。当前的滚动位置和所有元素的高度将直接附加到 this 。当使用 setState 时,您实际上只会在范围发生变化时进行重新渲染。



以下是一个示例无限列表的 http://jsfiddle.net/vjeux/KbWJ2/9/ 使用一些我在这个答案中描述的技术。这将是一些工作,但React绝对是一个实现无限列表的好方法:)


Our application uses infinite scrolling to navigate large lists of heterogenous items. There are a few wrinkles:

  • It's common for our users to have a list of 10,000 items and need to scroll through 3k+.
  • These are rich items, so we can only have a few hundred in the DOM before browser performance becomes unacceptable.
  • The items are of varying heights.
  • The items may contain images and we allow the user to jump to a specific date. This is tricky because the user can jump to a point in the list where we need to load images above the viewport, which would push the content down when they load. Failing to handle that means that the user may jump to a date, but then be shifted to an earlier date.

Known, incomplete solutions:

I'm not looking for the code for a complete solution (although that would be great.) Instead, I'm looking for the "React way" to model this situation. Is scroll position state or not? What state should I be tracking to retain my position in the list? What state do I need to keep so that I trigger a new render when I scroll near the bottom or top of what is rendered?

解决方案

This is a mix of an infinite table and an infinite scroll scenario. The best abstraction I found for this is the following:

Overview

Make a <List> component that takes an array of all children. Since we do not render them, it's really cheap to just allocate them and discard them. If 10k allocations is too big, you can instead pass a function that takes a range and return the elements.

<List>
  {thousandelements.map(function() { return <Element /> })}
</List>

Your List component is keeping track of what the scroll position is and only renders the children that are in view. It adds a large empty div at the beginning to fake the previous items that are not rendered.

Now, the interesting part is that once an Element component is rendered, you measure its height and store it in your List. This lets you compute the height of the spacer and know how many elements should be displayed in view.

Image

You are saying that when the image are loading they make everything "jump" down. The solution for this is to set the image dimensions in your img tag: <img src="..." width="100" height="58" />. This way the browser doesn't have to wait to download it before knowing what size it is going to be displayed. This requires some infrastructure but it's really worth it.

If you can't know the size in advance, then add onload listeners to your image and when it is loaded then measure its displayed dimension and update the stored row height and compensate the scroll position.

Jumping at a random element

If you need to jump at a random element in the list that's going to require some trickery with scroll position because you don't know the size of the elements in between. What I suggest you to do is to average the element heights you already have computed and jump to the scroll position of last known height + (number of elements * average).

Since this is not exact it's going to cause issues when you reach back to the last known good position. When a conflict happens, simply change the scroll position to fix it. This is going to move the scroll bar a bit but shouldn't affect him/her too much.

React Specifics

You want to provide a key to all the rendered elements so that they are maintained across renders. There are two strategies: (1) have only n keys (0, 1, 2, ... n) where n is the maximum number of elements you can display and use their position modulo n. (2) have a different key per element. If all the elements share a similar structure it's good to use (1) to reuse their DOM nodes. If they don't then use (2).

I would only have two pieces of React state: the index of the first element and the number of elements being displayed. The current scroll position and the height of all the elements would be directly attached to this. When using setState you are actually doing a rerender which should only happen when the range changes.

Here is an example http://jsfiddle.net/vjeux/KbWJ2/9/ of infinite list using some of the techniques I describe in this answer. It's going to be some work but React is definitively a good way to implement an infinite list :)

这篇关于ReactJS:建模双向无限滚动的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆