web
Managing the server side data (API request ..etc) is always a pain. We need to handle all possible case when we start fetching the data.
react-query is a perfect tool to make our life easier in such case.
In this case, we will utilize our existing project of "solana-dapp" since managing solana blockchain's complicated state is alwasy a pain.
We will make a useToken custom hook by utilizing useQuery to let all components to access registered splToken globally.
yarn add react-query # or
npm install react-query
Create src/store/tokenStore.tsx file. We will make the simple version first.
// src/store/tokenStore.tsx
import { TokenListProvider, TokenInfo } from "@solana/spl-token-registry";
import { useQuery } from "react-query";
export async function fetchSPLTokens() {
let rawTokenList = await new TokenListProvider().resolve();
return rawTokenList.filterByClusterSlug("mainnet-beta").getList();
}
export function useToken() {
return useQuery("tokens", fetchSPLTokens, {
refetchOnMount: false,
});
}
Super easy, now all components can access the data by using useToken. And useQuery will hanle all possible aync data fetching situation for us and return state to us. (ex. isLoading, error, status …etc)
Now we can try to access it on a component called SearchToken
So will need to have components/searchToken/index.tsx
import { TokenInfo } from "@solana/spl-token-registry";
import { useRef, useState } from "react";
import { useToken } from "../../store/tokenStore";
export default function SearchToken() {
const { data: tokenList, isLoading, isFetching } = useToken();
const [selectedToken, setSelectedToken] = useState<TokenInfo>();
const [filteredTokens, setFilteredTokens] = useState<TokenInfo[]>();
const inputRef = useRef<HTMLInputElement>(null);
function handleInput() {
let filtered = tokenList?.filter((t) =>
t.symbol.toUpperCase().startsWith(inputRef.current!.value.toUpperCase())
);
setFilteredTokens(filtered);
}
function handleSelectToken(token: TokenInfo) {
setSelectedToken(token);
}
if (isLoading || isLoading) {
return <div>Loading</div>;
}
return (
<>
<div
style={{
height: "50vh",
}}
>
<div style={{ height: "50vh", overflow: "scroll" }}>
<input
style={{ position: "fixed" }}
ref={inputRef}
onChange={handleInput}
placeholder={"Enter token symbol"}
/>
{!isLoading && (
<div>
{filteredTokens &&
filteredTokens.map((token, id) => (
<div key={id} onClick={() => handleSelectToken(token)}>
{token.symbol}
</div>
))}
</div>
)}
</div>
<div style={{ height: "30vh" }}>
{selectedToken && (
<div>
<div> Mint: {selectedToken.address}</div>
<div>Decimal: {selectedToken.decimals}</div>
<div>Symbol: {selectedToken.symbol}</div>
<div>Name: {selectedToken.name}</div>
<div>Token image: {selectedToken.logoURI}</div>
</div>
)}
</div>
</div>
</>
);
}
This is a very plain unstyled element, but you get how it works. We just make use of the useToken custom hook to get the data.