###### tags: `web`
# Use react-query as state manager in NextJS
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.
1. UseEffect: To decide when to update data
2. Many useState: To handle all kind of possible fetch states in order to show it on page (isLoading/ error/ refetch ...etc)
3. Create custom hook and provider to feed it to all compoents
**react-query** is a perfect tool to make our life easier in such case.
# Start with react-query with nextJS
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.
## Install react-query
```
yarn add react-query # or
npm install react-query
```
## The store lib and create the hook
Create src/store/tokenStore.tsx file. We will make the simple version first.
### Simple version
```
// 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.