Skip to content

Commit f67cc9d

Browse files
committed
feat(utils): add renderTimes util
1 parent 94f8826 commit f67cc9d

File tree

4 files changed

+136
-0
lines changed

4 files changed

+136
-0
lines changed

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,4 @@ export { useVisibilityEvent } from './hooks/useVisibilityEvent/index.ts';
2828
export { buildContext } from './utils/buildContext/index.ts';
2929
export { mergeProps } from './utils/mergeProps/index.ts';
3030
export { mergeRefs } from './utils/mergeRefs/index.ts';
31+
export { renderTimes } from './utils/renderTimes/index.ts';

src/utils/renderTimes/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { renderTimes } from './renderTimes.ts';
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { describe, expect, it } from 'vitest';
2+
3+
import { renderTimes } from './renderTimes.ts';
4+
5+
describe('renderTimes', () => {
6+
it('should render components the specified number of times', () => {
7+
const result = renderTimes(3, index => `Item ${index}`);
8+
9+
expect(result).toHaveLength(3);
10+
expect(result).toEqual(['Item 0', 'Item 1', 'Item 2']);
11+
});
12+
13+
it('should return empty array when count is 0', () => {
14+
const result = renderTimes(0, index => `Item ${index}`);
15+
16+
expect(result).toHaveLength(0);
17+
expect(result).toEqual([]);
18+
});
19+
20+
it('should handle negative count by returning empty array', () => {
21+
const result = renderTimes(-1, index => `Item ${index}`);
22+
23+
expect(result).toHaveLength(0);
24+
expect(result).toEqual([]);
25+
});
26+
27+
it('should call renderFunction with correct index for each iteration', () => {
28+
const indices: number[] = [];
29+
renderTimes(4, index => {
30+
indices.push(index);
31+
return index;
32+
});
33+
34+
expect(indices).toEqual([0, 1, 2, 3]);
35+
});
36+
37+
it('should work with React components', () => {
38+
const result = renderTimes(2, index => ({
39+
type: 'div',
40+
key: index.toString(),
41+
props: { children: `Component ${index}` },
42+
}));
43+
44+
expect(result).toHaveLength(2);
45+
expect(result[0]).toEqual({
46+
type: 'div',
47+
key: '0',
48+
props: {
49+
children: 'Component 0',
50+
},
51+
});
52+
expect(result[1]).toEqual({
53+
type: 'div',
54+
key: '1',
55+
props: {
56+
children: 'Component 1',
57+
},
58+
});
59+
});
60+
61+
it('should work with different return types', () => {
62+
const numberResult = renderTimes(3, index => index * 2);
63+
64+
expect(numberResult).toEqual([0, 2, 4]);
65+
66+
const booleanResult = renderTimes(2, index => index % 2 === 0);
67+
68+
expect(booleanResult).toEqual([true, false]);
69+
70+
const nullResult = renderTimes(2, () => null);
71+
72+
expect(nullResult).toEqual([null, null]);
73+
});
74+
});
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import type { ReactNode } from 'react';
2+
3+
/**
4+
* @description
5+
* `renderTimes` is a utility function that renders components a specified number of times.
6+
* It's useful for creating repeated UI elements like lists, grids, skeleton loaders, or pagination items
7+
* without manual duplication or verbose Array.from constructs.
8+
*
9+
* @param {number} count - The number of times to render the component. Returns empty array if count is 0 or negative.
10+
* @param {(index: number) => ReactNode} renderFunction - Function that receives the current index (0-based) and returns a ReactNode to render.
11+
*
12+
* @returns {ReactNode[]} An array of ReactNode elements.
13+
*
14+
* @example
15+
* // Basic list rendering
16+
* function ItemList() {
17+
* return (
18+
* <div>
19+
* {renderTimes(5, (index) => (
20+
* <div key={index}>Item {index + 1}</div>
21+
* ))}
22+
* </div>
23+
* );
24+
* }
25+
*
26+
* @example
27+
* // Skeleton loading state
28+
* function ProductSkeleton() {
29+
* return (
30+
* <div className="product-grid">
31+
* {renderTimes(8, (index) => (
32+
* <div key={index} className="product-card-skeleton">
33+
* <div className="skeleton-image" />
34+
* <div className="skeleton-title" />
35+
* <div className="skeleton-price" />
36+
* </div>
37+
* ))}
38+
* </div>
39+
* );
40+
* }
41+
*
42+
* @example
43+
* // Pagination dots
44+
* function PaginationDots({ totalSlides, currentSlide }) {
45+
* return (
46+
* <div className="pagination-dots">
47+
* {renderTimes(totalSlides, (index) => (
48+
* <button
49+
* key={index}
50+
* className={`dot ${index === currentSlide ? 'active' : ''}`}
51+
* onClick={() => goToSlide(index)}
52+
* />
53+
* ))}
54+
* </div>
55+
* );
56+
* }
57+
*/
58+
export function renderTimes(count: number, renderFunction: (index: number) => ReactNode): ReactNode[] {
59+
return Array.from({ length: count }, (_, index) => renderFunction(index));
60+
}

0 commit comments

Comments
 (0)