react-simple-virtualize 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +130 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/index.modern.mjs +2 -0
- package/dist/index.modern.mjs.map +1 -0
- package/dist/index.module.js +2 -0
- package/dist/index.module.js.map +1 -0
- package/dist/index.umd.js +2 -0
- package/dist/index.umd.js.map +1 -0
- package/package.json +39 -0
- package/src/index.d.ts +10 -0
- package/src/index.js +50 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ozan Batuhan Ceylan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# react-simple-virtualize
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
**react-simple-virtualize** is a lightweight (less than 1kb), dependency-free React hook for rendering large lists efficiently.
|
|
8
|
+
|
|
9
|
+
It solves the "too many DOM elements" problem by rendering only the items visible in the viewport. Perfect for scenarios where you need raw performance without the complexity or weight of larger libraries.
|
|
10
|
+
|
|
11
|
+
## 🚀 Features
|
|
12
|
+
|
|
13
|
+
* **Tiny:** < 1kb gzipped.
|
|
14
|
+
* **Zero Dependencies:** It does one thing and does it well.
|
|
15
|
+
* **Agnostic:** Works with `<div>`, `<ul>`, `<table>` or any other DOM element.
|
|
16
|
+
* **TypeScript:** Written in JS but includes full Type definitions.
|
|
17
|
+
|
|
18
|
+
## 📦 Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install react-simple-virtualize
|
|
22
|
+
# or
|
|
23
|
+
yarn add react-simple-virtualize
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## 💻 Usage
|
|
27
|
+
|
|
28
|
+
Here is a basic example of a list with 10,000 items:
|
|
29
|
+
|
|
30
|
+
```jsx
|
|
31
|
+
import React from 'react';
|
|
32
|
+
import { useVirtualize } from 'react-simple-virtualize';
|
|
33
|
+
|
|
34
|
+
const MyLargeList = () => {
|
|
35
|
+
// 1. Setup your data
|
|
36
|
+
const items = Array.from({ length: 10000 }, (_, i) => `Item ${i + 1}`);
|
|
37
|
+
|
|
38
|
+
// 2. Define dimensions
|
|
39
|
+
const containerHeight = 500;
|
|
40
|
+
const itemHeight = 50;
|
|
41
|
+
|
|
42
|
+
// 3. Initialize the hook
|
|
43
|
+
const { virtualItems, totalHeight, onScroll } = useVirtualize({
|
|
44
|
+
itemCount: items.length,
|
|
45
|
+
itemHeight,
|
|
46
|
+
containerHeight,
|
|
47
|
+
overscan: 5, // Optional: Number of items to render outside viewport
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
// Outer Container: Needs fixed height & overflow-y: auto
|
|
52
|
+
<div
|
|
53
|
+
onScroll={onScroll}
|
|
54
|
+
style={{
|
|
55
|
+
height: containerHeight,
|
|
56
|
+
overflowY: 'auto',
|
|
57
|
+
position: 'relative',
|
|
58
|
+
border: '1px solid #ccc'
|
|
59
|
+
}}
|
|
60
|
+
>
|
|
61
|
+
{/* Inner Container: Sets the scrollable height */}
|
|
62
|
+
<div style={{ height: totalHeight, position: 'relative' }}>
|
|
63
|
+
|
|
64
|
+
{/* Render only visible items */}
|
|
65
|
+
{virtualItems.map(({ index, offsetTop }) => (
|
|
66
|
+
<div
|
|
67
|
+
key={index}
|
|
68
|
+
style={{
|
|
69
|
+
position: 'absolute',
|
|
70
|
+
top: 0,
|
|
71
|
+
left: 0,
|
|
72
|
+
width: '100%',
|
|
73
|
+
height: itemHeight,
|
|
74
|
+
transform: `translateY(${offsetTop}px)`, // Crucial for positioning
|
|
75
|
+
display: 'flex',
|
|
76
|
+
alignItems: 'center',
|
|
77
|
+
paddingLeft: 10,
|
|
78
|
+
background: index % 2 === 0 ? '#fff' : '#f5f5f5'
|
|
79
|
+
}}
|
|
80
|
+
>
|
|
81
|
+
{items[index]}
|
|
82
|
+
</div>
|
|
83
|
+
))}
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export default MyLargeList;
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## ⚙️ API Reference
|
|
93
|
+
|
|
94
|
+
### `useVirtualize(options)`
|
|
95
|
+
|
|
96
|
+
| Option | Type | Required | Description |
|
|
97
|
+
| :--- | :--- | :--- | :--- |
|
|
98
|
+
| `itemCount` | `number` | Yes | Total number of items in the list. |
|
|
99
|
+
| `itemHeight` | `number` | Yes | Height of a single item in pixels. |
|
|
100
|
+
| `containerHeight` | `number` | Yes | Height of the visible scroll area (viewport). |
|
|
101
|
+
| `overscan` | `number` | No | Number of extra items to render above/below the visible area (Default: 3). |
|
|
102
|
+
|
|
103
|
+
### Returns
|
|
104
|
+
|
|
105
|
+
| Value | Type | Description |
|
|
106
|
+
| :--- | :--- | :--- |
|
|
107
|
+
| `virtualItems` | `array` | Array of items to be rendered. Each item contains `{ index, offsetTop }`. |
|
|
108
|
+
| `totalHeight` | `number` | Total height of the list (used for the inner container). |
|
|
109
|
+
| `onScroll` | `function` | Event handler to be attached to the outer container's `onScroll`. |
|
|
110
|
+
|
|
111
|
+
## 🗺️ Roadmap & Support
|
|
112
|
+
|
|
113
|
+
Currently, this package is optimized for **Fixed Height** lists.
|
|
114
|
+
I am actively working on the following features for the next major release:
|
|
115
|
+
|
|
116
|
+
- [ ] **Dynamic Height Support:** For items with variable content (chat bubbles, feeds).
|
|
117
|
+
- [ ] **Grid Virtualization:** Virtualizing both rows and columns.
|
|
118
|
+
- [ ] **Horizontal Scrolling:** Support for X-axis virtualization.
|
|
119
|
+
|
|
120
|
+
### ☕ Support the Development
|
|
121
|
+
|
|
122
|
+
If this package saved you time or if you want to speed up the development of **Dynamic Height** support, consider buying me a coffee. It helps keep the project alive and maintained!
|
|
123
|
+
|
|
124
|
+
<a href="https://www.buymeacoffee.com/obceylan" target="_blank">
|
|
125
|
+
<img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 60px !important;width: 217px !important;" >
|
|
126
|
+
</a>
|
|
127
|
+
|
|
128
|
+
## 📄 License
|
|
129
|
+
|
|
130
|
+
MIT © Ozan Batuhan Ceylan
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var t=require("react");exports.useVirtualize=function(e){var r=e.itemCount,o=e.itemHeight,a=e.containerHeight,i=e.overscan,n=void 0===i?3:i,u=t.useState(0),l=u[0],h=u[1],s=t.useMemo(function(){for(var t=Math.floor(l/o),e=Math.min(r-1,Math.floor((l+a)/o)),i=Math.max(0,t-n),u=Math.min(r-1,e+n),h=[],s=i;s<=u;s++)h.push({index:s,offsetTop:s*o});return{virtualItems:h,totalHeight:r*o}},[l,r,o,a,n]);return{virtualItems:s.virtualItems,totalHeight:s.totalHeight,onScroll:function(t){h(t.currentTarget.scrollTop)}}};
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/index.js"],"sourcesContent":["import { useState, useMemo } from 'react';\n\nexport const useVirtualize = ({\n itemCount,\n itemHeight,\n containerHeight,\n overscan = 3\n}) => {\n // Store the current scroll position\n const [scrollTop, setScrollTop] = useState(0);\n\n // Update scroll position on scroll event\n const onScroll = (e) => {\n setScrollTop(e.currentTarget.scrollTop);\n };\n\n // Memoize calculations to prevent unnecessary re-renders\n const { virtualItems, totalHeight } = useMemo(() => {\n // 1. Calculate the visible range based on scroll position\n const rangeStart = Math.floor(scrollTop / itemHeight);\n const rangeEnd = Math.min(\n itemCount - 1,\n Math.floor((scrollTop + containerHeight) / itemHeight)\n );\n\n // 2. Add overscan (buffer) to the range for smoother scrolling\n const startIndex = Math.max(0, rangeStart - overscan);\n const endIndex = Math.min(itemCount - 1, rangeEnd + overscan);\n\n // 3. Generate the array of items to be rendered\n const virtualItems = [];\n for (let i = startIndex; i <= endIndex; i++) {\n virtualItems.push({\n index: i,\n offsetTop: i * itemHeight, // Calculate absolute position for each item\n });\n }\n\n // 4. Calculate total phantom height to maintain scrollbar size\n const totalHeight = itemCount * itemHeight;\n\n return { virtualItems, totalHeight };\n }, [scrollTop, itemCount, itemHeight, containerHeight, overscan]);\n\n return {\n virtualItems,\n totalHeight,\n onScroll,\n };\n};"],"names":["_ref","itemCount","itemHeight","containerHeight","_ref$overscan","overscan","_useState","useState","scrollTop","setScrollTop","_useMemo","useMemo","rangeStart","Math","floor","rangeEnd","min","startIndex","max","endIndex","virtualItems","i","push","index","offsetTop","totalHeight","onScroll","e","currentTarget"],"mappings":"6CAE6B,SAAHA,GAKpB,IAJJC,EAASD,EAATC,UACAC,EAAUF,EAAVE,WACAC,EAAeH,EAAfG,gBAAeC,EAAAJ,EACfK,SAAAA,OAAQ,IAAAD,EAAG,EAACA,EAGZE,EAAkCC,EAAQA,SAAC,GAApCC,EAASF,EAAEG,GAAAA,EAAYH,EAG9B,GAKAI,EAAsCC,EAAAA,QAAQ,WAc5C,IAZA,IAAMC,EAAaC,KAAKC,MAAMN,EAAYN,GACpCa,EAAWF,KAAKG,IACpBf,EAAY,EACZY,KAAKC,OAAON,EAAYL,GAAmBD,IAIvCe,EAAaJ,KAAKK,IAAI,EAAGN,EAAaP,GACtCc,EAAWN,KAAKG,IAAIf,EAAY,EAAGc,EAAWV,GAG9Ce,EAAe,GACZC,EAAIJ,EAAYI,GAAKF,EAAUE,IACtCD,EAAaE,KAAK,CAChBC,MAAOF,EACPG,UAAWH,EAAInB,IAOnB,MAAO,CAAEkB,aAAAA,EAAcK,YAFHxB,EAAYC,EAGlC,EAAG,CAACM,EAAWP,EAAWC,EAAYC,EAAiBE,IAEvD,MAAO,CACLe,aA5BkBV,EAAZU,aA6BNK,YA7B+Bf,EAAXe,YA8BpBC,SAnCe,SAACC,GAChBlB,EAAakB,EAAEC,cAAcpB,UAC/B,EAmCF"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{useState as t,useMemo as o}from"react";const e=({itemCount:e,itemHeight:r,containerHeight:a,overscan:i=3})=>{const[n,l]=t(0),{virtualItems:h,totalHeight:m}=o(()=>{const t=Math.floor(n/r),o=Math.min(e-1,Math.floor((n+a)/r)),l=Math.max(0,t-i),h=Math.min(e-1,o+i),m=[];for(let t=l;t<=h;t++)m.push({index:t,offsetTop:t*r});return{virtualItems:m,totalHeight:e*r}},[n,e,r,a,i]);return{virtualItems:h,totalHeight:m,onScroll:t=>{l(t.currentTarget.scrollTop)}}};export{e as useVirtualize};
|
|
2
|
+
//# sourceMappingURL=index.modern.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.modern.mjs","sources":["../src/index.js"],"sourcesContent":["import { useState, useMemo } from 'react';\n\nexport const useVirtualize = ({\n itemCount,\n itemHeight,\n containerHeight,\n overscan = 3\n}) => {\n // Store the current scroll position\n const [scrollTop, setScrollTop] = useState(0);\n\n // Update scroll position on scroll event\n const onScroll = (e) => {\n setScrollTop(e.currentTarget.scrollTop);\n };\n\n // Memoize calculations to prevent unnecessary re-renders\n const { virtualItems, totalHeight } = useMemo(() => {\n // 1. Calculate the visible range based on scroll position\n const rangeStart = Math.floor(scrollTop / itemHeight);\n const rangeEnd = Math.min(\n itemCount - 1,\n Math.floor((scrollTop + containerHeight) / itemHeight)\n );\n\n // 2. Add overscan (buffer) to the range for smoother scrolling\n const startIndex = Math.max(0, rangeStart - overscan);\n const endIndex = Math.min(itemCount - 1, rangeEnd + overscan);\n\n // 3. Generate the array of items to be rendered\n const virtualItems = [];\n for (let i = startIndex; i <= endIndex; i++) {\n virtualItems.push({\n index: i,\n offsetTop: i * itemHeight, // Calculate absolute position for each item\n });\n }\n\n // 4. Calculate total phantom height to maintain scrollbar size\n const totalHeight = itemCount * itemHeight;\n\n return { virtualItems, totalHeight };\n }, [scrollTop, itemCount, itemHeight, containerHeight, overscan]);\n\n return {\n virtualItems,\n totalHeight,\n onScroll,\n };\n};"],"names":["useVirtualize","itemCount","itemHeight","containerHeight","overscan","scrollTop","setScrollTop","useState","virtualItems","totalHeight","useMemo","rangeStart","Math","floor","rangeEnd","min","startIndex","max","endIndex","i","push","index","offsetTop","onScroll","e","currentTarget"],"mappings":"8CAEa,MAAAA,EAAgBA,EAC3BC,YACAC,aACAC,kBACAC,SAAAA,EAAW,MAGX,MAAOC,EAAWC,GAAgBC,EAAS,IAQrCC,aAAEA,EAAYC,YAAEA,GAAgBC,EAAQ,KAE5C,MAAMC,EAAaC,KAAKC,MAAMR,EAAYH,GACpCY,EAAWF,KAAKG,IACpBd,EAAY,EACZW,KAAKC,OAAOR,EAAYF,GAAmBD,IAIvCc,EAAaJ,KAAKK,IAAI,EAAGN,EAAaP,GACtCc,EAAWN,KAAKG,IAAId,EAAY,EAAGa,EAAWV,GAG9CI,EAAe,GACrB,IAAK,IAAIW,EAAIH,EAAYG,GAAKD,EAAUC,IACtCX,EAAaY,KAAK,CAChBC,MAAOF,EACPG,UAAWH,EAAIjB,IAOnB,MAAO,CAAEM,eAAcC,YAFHR,EAAYC,IAG/B,CAACG,EAAWJ,EAAWC,EAAYC,EAAiBC,IAEvD,MAAO,CACLI,eACAC,cACAc,SAnCgBC,IAChBlB,EAAakB,EAAEC,cAAcpB"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{useState as t,useMemo as o}from"react";var r=function(r){var e=r.itemCount,a=r.itemHeight,i=r.containerHeight,n=r.overscan,l=void 0===n?3:n,h=t(0),u=h[0],m=h[1],c=o(function(){for(var t=Math.floor(u/a),o=Math.min(e-1,Math.floor((u+i)/a)),r=Math.max(0,t-l),n=Math.min(e-1,o+l),h=[],m=r;m<=n;m++)h.push({index:m,offsetTop:m*a});return{virtualItems:h,totalHeight:e*a}},[u,e,a,i,l]);return{virtualItems:c.virtualItems,totalHeight:c.totalHeight,onScroll:function(t){m(t.currentTarget.scrollTop)}}};export{r as useVirtualize};
|
|
2
|
+
//# sourceMappingURL=index.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.module.js","sources":["../src/index.js"],"sourcesContent":["import { useState, useMemo } from 'react';\n\nexport const useVirtualize = ({\n itemCount,\n itemHeight,\n containerHeight,\n overscan = 3\n}) => {\n // Store the current scroll position\n const [scrollTop, setScrollTop] = useState(0);\n\n // Update scroll position on scroll event\n const onScroll = (e) => {\n setScrollTop(e.currentTarget.scrollTop);\n };\n\n // Memoize calculations to prevent unnecessary re-renders\n const { virtualItems, totalHeight } = useMemo(() => {\n // 1. Calculate the visible range based on scroll position\n const rangeStart = Math.floor(scrollTop / itemHeight);\n const rangeEnd = Math.min(\n itemCount - 1,\n Math.floor((scrollTop + containerHeight) / itemHeight)\n );\n\n // 2. Add overscan (buffer) to the range for smoother scrolling\n const startIndex = Math.max(0, rangeStart - overscan);\n const endIndex = Math.min(itemCount - 1, rangeEnd + overscan);\n\n // 3. Generate the array of items to be rendered\n const virtualItems = [];\n for (let i = startIndex; i <= endIndex; i++) {\n virtualItems.push({\n index: i,\n offsetTop: i * itemHeight, // Calculate absolute position for each item\n });\n }\n\n // 4. Calculate total phantom height to maintain scrollbar size\n const totalHeight = itemCount * itemHeight;\n\n return { virtualItems, totalHeight };\n }, [scrollTop, itemCount, itemHeight, containerHeight, overscan]);\n\n return {\n virtualItems,\n totalHeight,\n onScroll,\n };\n};"],"names":["useVirtualize","_ref","itemCount","itemHeight","containerHeight","_ref$overscan","overscan","_useState","useState","scrollTop","setScrollTop","_useMemo","useMemo","rangeStart","Math","floor","rangeEnd","min","startIndex","max","endIndex","virtualItems","i","push","index","offsetTop","totalHeight","onScroll","e","currentTarget"],"mappings":"8CAEa,IAAAA,EAAgB,SAAHC,GAKpB,IAJJC,EAASD,EAATC,UACAC,EAAUF,EAAVE,WACAC,EAAeH,EAAfG,gBAAeC,EAAAJ,EACfK,SAAAA,OAAQ,IAAAD,EAAG,EAACA,EAGZE,EAAkCC,EAAS,GAApCC,EAASF,EAAEG,GAAAA,EAAYH,EAG9B,GAKAI,EAAsCC,EAAQ,WAc5C,IAZA,IAAMC,EAAaC,KAAKC,MAAMN,EAAYN,GACpCa,EAAWF,KAAKG,IACpBf,EAAY,EACZY,KAAKC,OAAON,EAAYL,GAAmBD,IAIvCe,EAAaJ,KAAKK,IAAI,EAAGN,EAAaP,GACtCc,EAAWN,KAAKG,IAAIf,EAAY,EAAGc,EAAWV,GAG9Ce,EAAe,GACZC,EAAIJ,EAAYI,GAAKF,EAAUE,IACtCD,EAAaE,KAAK,CAChBC,MAAOF,EACPG,UAAWH,EAAInB,IAOnB,MAAO,CAAEkB,aAAAA,EAAcK,YAFHxB,EAAYC,EAGlC,EAAG,CAACM,EAAWP,EAAWC,EAAYC,EAAiBE,IAEvD,MAAO,CACLe,aA5BkBV,EAAZU,aA6BNK,YA7B+Bf,EAAXe,YA8BpBC,SAnCe,SAACC,GAChBlB,EAAakB,EAAEC,cAAcpB,UAC/B,EAmCF"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("react")):"function"==typeof define&&define.amd?define(["exports","react"],e):e((t||self).reactSimpleVirtualize={},t.react)}(this,function(t,e){t.useVirtualize=function(t){var o=t.itemCount,i=t.itemHeight,n=t.containerHeight,r=t.overscan,a=void 0===r?3:r,u=e.useState(0),f=u[0],l=u[1],s=e.useMemo(function(){for(var t=Math.floor(f/i),e=Math.min(o-1,Math.floor((f+n)/i)),r=Math.max(0,t-a),u=Math.min(o-1,e+a),l=[],s=r;s<=u;s++)l.push({index:s,offsetTop:s*i});return{virtualItems:l,totalHeight:o*i}},[f,o,i,n,a]);return{virtualItems:s.virtualItems,totalHeight:s.totalHeight,onScroll:function(t){l(t.currentTarget.scrollTop)}}}});
|
|
2
|
+
//# sourceMappingURL=index.umd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.umd.js","sources":["../src/index.js"],"sourcesContent":["import { useState, useMemo } from 'react';\n\nexport const useVirtualize = ({\n itemCount,\n itemHeight,\n containerHeight,\n overscan = 3\n}) => {\n // Store the current scroll position\n const [scrollTop, setScrollTop] = useState(0);\n\n // Update scroll position on scroll event\n const onScroll = (e) => {\n setScrollTop(e.currentTarget.scrollTop);\n };\n\n // Memoize calculations to prevent unnecessary re-renders\n const { virtualItems, totalHeight } = useMemo(() => {\n // 1. Calculate the visible range based on scroll position\n const rangeStart = Math.floor(scrollTop / itemHeight);\n const rangeEnd = Math.min(\n itemCount - 1,\n Math.floor((scrollTop + containerHeight) / itemHeight)\n );\n\n // 2. Add overscan (buffer) to the range for smoother scrolling\n const startIndex = Math.max(0, rangeStart - overscan);\n const endIndex = Math.min(itemCount - 1, rangeEnd + overscan);\n\n // 3. Generate the array of items to be rendered\n const virtualItems = [];\n for (let i = startIndex; i <= endIndex; i++) {\n virtualItems.push({\n index: i,\n offsetTop: i * itemHeight, // Calculate absolute position for each item\n });\n }\n\n // 4. Calculate total phantom height to maintain scrollbar size\n const totalHeight = itemCount * itemHeight;\n\n return { virtualItems, totalHeight };\n }, [scrollTop, itemCount, itemHeight, containerHeight, overscan]);\n\n return {\n virtualItems,\n totalHeight,\n onScroll,\n };\n};"],"names":["_ref","itemCount","itemHeight","containerHeight","_ref$overscan","overscan","_useState","useState","scrollTop","setScrollTop","_useMemo","useMemo","rangeStart","Math","floor","rangeEnd","min","startIndex","max","endIndex","virtualItems","i","push","index","offsetTop","totalHeight","onScroll","e","currentTarget"],"mappings":"kSAE6B,SAAHA,GAKpB,IAJJC,EAASD,EAATC,UACAC,EAAUF,EAAVE,WACAC,EAAeH,EAAfG,gBAAeC,EAAAJ,EACfK,SAAAA,OAAQ,IAAAD,EAAG,EAACA,EAGZE,EAAkCC,EAAQA,SAAC,GAApCC,EAASF,EAAEG,GAAAA,EAAYH,EAG9B,GAKAI,EAAsCC,EAAAA,QAAQ,WAc5C,IAZA,IAAMC,EAAaC,KAAKC,MAAMN,EAAYN,GACpCa,EAAWF,KAAKG,IACpBf,EAAY,EACZY,KAAKC,OAAON,EAAYL,GAAmBD,IAIvCe,EAAaJ,KAAKK,IAAI,EAAGN,EAAaP,GACtCc,EAAWN,KAAKG,IAAIf,EAAY,EAAGc,EAAWV,GAG9Ce,EAAe,GACZC,EAAIJ,EAAYI,GAAKF,EAAUE,IACtCD,EAAaE,KAAK,CAChBC,MAAOF,EACPG,UAAWH,EAAInB,IAOnB,MAAO,CAAEkB,aAAAA,EAAcK,YAFHxB,EAAYC,EAGlC,EAAG,CAACM,EAAWP,EAAWC,EAAYC,EAAiBE,IAEvD,MAAO,CACLe,aA5BkBV,EAAZU,aA6BNK,YA7B+Bf,EAAXe,YA8BpBC,SAnCe,SAACC,GAChBlB,EAAakB,EAAEC,cAAcpB,UAC/B,EAmCF"}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "react-simple-virtualize",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "A lightweight, dependency-free virtualization hook for React.",
|
|
5
|
+
"source": "src/index.js",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"module": "dist/index.module.js",
|
|
8
|
+
"unpkg": "dist/index.umd.js",
|
|
9
|
+
"types": "src/index.d.ts",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "microbundle",
|
|
12
|
+
"dev": "microbundle watch",
|
|
13
|
+
"prepublishOnly": "npm run build"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"react",
|
|
17
|
+
"virtual-list",
|
|
18
|
+
"virtualization",
|
|
19
|
+
"scroll",
|
|
20
|
+
"performance",
|
|
21
|
+
"hook"
|
|
22
|
+
],
|
|
23
|
+
"author": "Ozan Batuhan Ceylan",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "https://github.com/ozanbatuhanceylan/react-simple-virtualize"
|
|
28
|
+
},
|
|
29
|
+
"funding": {
|
|
30
|
+
"type": "buymeacoffee",
|
|
31
|
+
"url": "https://www.buymeacoffee.com/obceylan"
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"react": ">=16.8.0"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"microbundle": "^0.15.1"
|
|
38
|
+
}
|
|
39
|
+
}
|
package/src/index.d.ts
ADDED
package/src/index.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { useState, useMemo } from 'react';
|
|
2
|
+
|
|
3
|
+
export const useVirtualize = ({
|
|
4
|
+
itemCount,
|
|
5
|
+
itemHeight,
|
|
6
|
+
containerHeight,
|
|
7
|
+
overscan = 3
|
|
8
|
+
}) => {
|
|
9
|
+
// Store the current scroll position
|
|
10
|
+
const [scrollTop, setScrollTop] = useState(0);
|
|
11
|
+
|
|
12
|
+
// Update scroll position on scroll event
|
|
13
|
+
const onScroll = (e) => {
|
|
14
|
+
setScrollTop(e.currentTarget.scrollTop);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// Memoize calculations to prevent unnecessary re-renders
|
|
18
|
+
const { virtualItems, totalHeight } = useMemo(() => {
|
|
19
|
+
// 1. Calculate the visible range based on scroll position
|
|
20
|
+
const rangeStart = Math.floor(scrollTop / itemHeight);
|
|
21
|
+
const rangeEnd = Math.min(
|
|
22
|
+
itemCount - 1,
|
|
23
|
+
Math.floor((scrollTop + containerHeight) / itemHeight)
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
// 2. Add overscan (buffer) to the range for smoother scrolling
|
|
27
|
+
const startIndex = Math.max(0, rangeStart - overscan);
|
|
28
|
+
const endIndex = Math.min(itemCount - 1, rangeEnd + overscan);
|
|
29
|
+
|
|
30
|
+
// 3. Generate the array of items to be rendered
|
|
31
|
+
const virtualItems = [];
|
|
32
|
+
for (let i = startIndex; i <= endIndex; i++) {
|
|
33
|
+
virtualItems.push({
|
|
34
|
+
index: i,
|
|
35
|
+
offsetTop: i * itemHeight, // Calculate absolute position for each item
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// 4. Calculate total phantom height to maintain scrollbar size
|
|
40
|
+
const totalHeight = itemCount * itemHeight;
|
|
41
|
+
|
|
42
|
+
return { virtualItems, totalHeight };
|
|
43
|
+
}, [scrollTop, itemCount, itemHeight, containerHeight, overscan]);
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
virtualItems,
|
|
47
|
+
totalHeight,
|
|
48
|
+
onScroll,
|
|
49
|
+
};
|
|
50
|
+
};
|