mirror of
https://github.com/nextui-org/nextui.git
synced 2025-12-08 19:26:11 +00:00
188 lines
4.5 KiB
TypeScript
188 lines
4.5 KiB
TypeScript
const usePokemonListTs = `export type Pokemon = {
|
|
name: string;
|
|
url: string;
|
|
};
|
|
|
|
export type UsePokemonListProps = {
|
|
/** Delay to wait before fetching more items */
|
|
fetchDelay?: number;
|
|
};
|
|
|
|
export function usePokemonList({fetchDelay = 0}: UsePokemonListProps = {}) {
|
|
const [items, setItems] = React.useState<Pokemon[]>([]);
|
|
const [hasMore, setHasMore] = React.useState(true);
|
|
const [isLoading, setIsLoading] = React.useState(false);
|
|
const [offset, setOffset] = React.useState(0);
|
|
const limit = 10; // Number of items per page, adjust as necessary
|
|
|
|
const loadPokemon = async (currentOffset: number) => {
|
|
const controller = new AbortController();
|
|
const {signal} = controller;
|
|
|
|
try {
|
|
setIsLoading(true);
|
|
|
|
if (offset > 0) {
|
|
// Delay to simulate network latency
|
|
await new Promise((resolve) => setTimeout(resolve, fetchDelay));
|
|
}
|
|
|
|
let res = await fetch(
|
|
\`https://pokeapi.co/api/v2/pokemon?offset=\${currentOffset}&limit=\${limit}\`,
|
|
{signal},
|
|
);
|
|
|
|
if (!res.ok) {
|
|
throw new Error("Network response was not ok");
|
|
}
|
|
|
|
let json = await res.json();
|
|
|
|
setHasMore(json.next !== null);
|
|
// Append new results to existing ones
|
|
setItems((prevItems) => [...prevItems, ...json.results]);
|
|
} catch (error) {
|
|
if (error.name === "AbortError") {
|
|
console.log("Fetch aborted");
|
|
} else {
|
|
console.error("There was an error with the fetch operation:", error);
|
|
}
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
React.useEffect(() => {
|
|
loadPokemon(offset);
|
|
}, []);
|
|
|
|
const onLoadMore = () => {
|
|
const newOffset = offset + limit;
|
|
|
|
setOffset(newOffset);
|
|
loadPokemon(newOffset);
|
|
};
|
|
|
|
return {
|
|
items,
|
|
hasMore,
|
|
isLoading,
|
|
onLoadMore,
|
|
};
|
|
}
|
|
|
|
`;
|
|
|
|
const usePokemonList = `export function usePokemonList({fetchDelay = 0} = {}) {
|
|
const [items, setItems] = React.useState([]);
|
|
const [hasMore, setHasMore] = React.useState(true);
|
|
const [isLoading, setIsLoading] = React.useState(false);
|
|
const [offset, setOffset] = React.useState(0);
|
|
const limit = 10; // Number of items per page, adjust as necessary
|
|
|
|
const loadPokemon = async (currentOffset) => {
|
|
const controller = new AbortController();
|
|
const {signal} = controller;
|
|
|
|
try {
|
|
setIsLoading(true);
|
|
|
|
if (offset > 0) {
|
|
// Delay to simulate network latency
|
|
await new Promise((resolve) => setTimeout(resolve, fetchDelay));
|
|
}
|
|
|
|
let res = await fetch(
|
|
\`https://pokeapi.co/api/v2/pokemon?offset=\${currentOffset}&limit=\${limit}\`,
|
|
{signal},
|
|
);
|
|
|
|
if (!res.ok) {
|
|
throw new Error("Network response was not ok");
|
|
}
|
|
|
|
let json = await res.json();
|
|
|
|
setHasMore(json.next !== null);
|
|
// Append new results to existing ones
|
|
setItems((prevItems) => [...prevItems, ...json.results]);
|
|
} catch (error) {
|
|
if (error.name === "AbortError") {
|
|
console.log("Fetch aborted");
|
|
} else {
|
|
console.error("There was an error with the fetch operation:", error);
|
|
}
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
React.useEffect(() => {
|
|
loadPokemon(offset);
|
|
}, []);
|
|
|
|
const onLoadMore = () => {
|
|
const newOffset = offset + limit;
|
|
|
|
setOffset(newOffset);
|
|
loadPokemon(newOffset);
|
|
};
|
|
|
|
return {
|
|
items,
|
|
hasMore,
|
|
isLoading,
|
|
onLoadMore,
|
|
};
|
|
};`;
|
|
|
|
const App = `import {Select, SelectItem} from "@nextui-org/react";
|
|
import {useInfiniteScroll} from "@nextui-org/use-infinite-scroll";
|
|
import {usePokemonList} from "./usePokemonList";
|
|
|
|
export default function App() {
|
|
const [isOpen, setIsOpen] = React.useState(false);
|
|
const {items, hasMore, isLoading, onLoadMore} = usePokemonList({fetchDelay: 1500});
|
|
|
|
const [, scrollerRef] = useInfiniteScroll({
|
|
hasMore,
|
|
isEnabled: isOpen,
|
|
shouldUseLoader: false, // We don't want to show the loader at the bottom of the list
|
|
onLoadMore,
|
|
});
|
|
|
|
return (
|
|
<Select
|
|
className="max-w-xs"
|
|
isLoading={isLoading}
|
|
items={items}
|
|
label="Pick a Pokemon"
|
|
placeholder="Select a Pokemon"
|
|
scrollRef={scrollerRef}
|
|
selectionMode="single"
|
|
onOpenChange={setIsOpen}
|
|
>
|
|
{(item) => (
|
|
<SelectItem key={item.name} className="capitalize">
|
|
{item.name}
|
|
</SelectItem>
|
|
)}
|
|
</Select>
|
|
);
|
|
}`;
|
|
|
|
const react = {
|
|
"/App.jsx": App,
|
|
"/usePokemonList.js": usePokemonList,
|
|
};
|
|
|
|
const reactTs = {
|
|
"/App.tsx": App,
|
|
"/usePokemonList.ts": usePokemonListTs,
|
|
};
|
|
|
|
export default {
|
|
...react,
|
|
...reactTs,
|
|
};
|