@gechiui/compose
Version:
GeChiUI higher-order components (HOCs).
81 lines (69 loc) • 1.87 kB
text/typescript
/**
* GeChiUI dependencies
*/
import { useEffect, useState } from '@gechiui/element';
import { createQueue } from '@gechiui/priority-queue';
type AsyncListConfig = {
step: number;
};
/**
* Returns the first items from list that are present on state.
*
* @param list New array.
* @param state Current state.
* @return First items present iin state.
*/
function getFirstItemsPresentInState< T >( list: T[], state: T[] ): T[] {
const firstItems = [];
for ( let i = 0; i < list.length; i++ ) {
const item = list[ i ];
if ( ! state.includes( item ) ) {
break;
}
firstItems.push( item );
}
return firstItems;
}
/**
* React hook returns an array which items get asynchronously appended from a source array.
* This behavior is useful if we want to render a list of items asynchronously for performance reasons.
*
* @param list Source array.
* @param config Configuration object.
*
* @return Async array.
*/
function useAsyncList< T >(
list: T[],
config: AsyncListConfig = { step: 1 }
): T[] {
const { step = 1 } = config;
const [ current, setCurrent ] = useState( [] as T[] );
useEffect( () => {
// On reset, we keep the first items that were previously rendered.
let firstItems = getFirstItemsPresentInState( list, current );
if ( firstItems.length < step ) {
firstItems = firstItems.concat(
list.slice( firstItems.length, step )
);
}
setCurrent( firstItems );
let nextIndex = firstItems.length;
const asyncQueue = createQueue();
const append = () => {
if ( list.length <= nextIndex ) {
return;
}
setCurrent( ( state ) => [
...state,
...list.slice( nextIndex, nextIndex + step ),
] );
nextIndex += step;
asyncQueue.add( {}, append );
};
asyncQueue.add( {}, append );
return () => asyncQueue.reset();
}, [ list ] );
return current;
}
export default useAsyncList;