'React DOM update on fetch
I'm working on a typescript react project to add and display some accounts on a wallet.. I have created a useAPI function that makes my calls and returns the state of the call as well as the retrieved data
api/index.ts
import { useCallback, useEffect, useState } from "react";
export type ApiMethod = "GET" | "POST";
export type ApiState = "idle" | "loading" | "done" | "failed";
const fetcher = async (
url: string,
method: ApiMethod,
payload?: string,
): Promise<any> => {
const requestHeaders = new Headers();
requestHeaders.set("Content-Type", "application/json");
const res = await fetch(url, {
body: payload ? JSON.stringify(payload) : undefined,
headers: requestHeaders,
method,
});
//const resobj = await res.json();
return res;
};
export function useApi(
url: string,
method: ApiMethod,
payload?: any
): {
apiState: ApiState;
data: [];
execute: () => void;
} {
const [apiState, setApiState] = useState<ApiState>("idle");
const [data, setData] = useState<[]>([]);
const [toCallApi, setApiExecution] = useState(false);
const execute = () => {
console.log("executing api call");
setApiExecution(true);
};
const fetchApi = useCallback(async () => {
fetcher(url, method, payload)
.then((res) => {
if(!res.ok){
return "error"
}
return res.json()
})
.then((result) => {
if(result === "error") setApiState("failed")
else{
setData(result)
setApiState("done")
}
})
}, [method, payload, url]);
// call api
useEffect(() => {
if (toCallApi && apiState === "idle") {
console.log("calling api");
setApiState("loading");
fetchApi();
}
}, [apiState, fetchApi, toCallApi]);
console.log(url,data, apiState, payload)
return {
apiState,
data,
execute,
};
}
After this I call this function on my app.tsx first inside a useEffect hook to retrieve the accounts when my DOM loads.
And then I have the possibility to create a new Account, and display with the previous accounts.
PROBLEM:
The problem here is, when I create a new Account or when I get a failed state, I cannot display the account or the error message until I reload my page.
Though I need to update my DOM once I create an account or get an error to actually display it.
app.tsx
function App() {
const [selectedCurrency, setSelectedCurrency] = useState("")
const [errorIsOpen, setErrorIsOpen] = useState(false)
const accounts = useApi(
`${process.env.REACT_APP_PROXY}/accounts`,
"GET",
)
const wallets = useApi(
`${process.env.REACT_APP_PROXY}/wallets`,
"GET",
)
const createWallet = useApi(
`${process.env.REACT_APP_PROXY}/accounts`,
"POST",
{currency: selectedCurrency}
)
const [isOpen, setIsOpen] = useState(false)
useEffect(() => {
accounts.execute()
}, [accounts])
useEffect(() => {
if(isOpen){
wallets.execute()
}
}, [isOpen, wallets])
const handleWalletCreate = async () => {
createWallet.execute()
accounts.execute()
if(createWallet.apiState === "failed"){
setErrorIsOpen(true)
}
}
return (
<>
<Modal isOpen={isOpen}>
{
wallets.apiState === "loading" &&
<div className='center'>
<Loader size={100} width={8}/>
</div>
}
{
(wallets.apiState === "done" && wallets.data) &&
<div className="modal-content">
<div className='modal-content-header'>
<h3>Add new wallet</h3>
<span onClick={() => setIsOpen(!isOpen)}>
<Close />
</span>
</div>
<div className='info'>
<p>
The crypto wallet will be created instantly and be available in your list of wallets.
</p>
</div>
<div className='form'>
<h4>Select wallet</h4>
<select defaultValue="DEFAULT" onChange={(e) => setSelectedCurrency(e.target.value)}>
<option value="DEFAULT" disabled>Select currency</option>
{wallets.data.map((wallet: any, i) =>
<option key={i} value={wallet.currency}>{wallet.name}</option>
)}
</select>
<div className='btn-container'>
<button className='btn-submit' onClick={handleWalletCreate}>
Create wallet
</button>
</div>
</div>
{
(createWallet.apiState === "failed" || errorIsOpen) &&
<div className="create-wallet-error">
<div>
<NetworkError />
<span>Network Error</span>
</div>
<CloseError onClick={() => setErrorIsOpen(!errorIsOpen)}/>
</div>
}
</div>
}
{
wallets.apiState === "failed" &&
<div className='center'>
<div className='network-error'>
<div className='error-logo'>
<ErrorIcon />
</div>
<h3>Network Error</h3>
<div className="btn-container">
<button className="btn-submit" onClick={wallets.execute}>
Try again
</button>
</div>
</div>
</div>
}
</Modal>
<div className="main">
<header>
<div className="header-container">
<div className='logo'>
<Logo />
</div>
<div className='profile'>
<span className='profile-picture'>
O
</span>
<h3 className="profile-name">
Oluwatobi Akindunjoye
</h3>
</div>
</div>
</header>
<section className='dashboard'>
<div className='dashboard-container'>
<div className='left-section'>
<ul>
<li>Wallets</li>
<li>Prices</li>
<li>Peer2Peer</li>
<li>Activity</li>
<li>Settings</li>
</ul>
</div>
<div className='right-section'>
<div className='right-section-header'>
<h1>Wallets</h1>
<span onClick={() => setIsOpen(!isOpen)} className={accounts.apiState === "failed" ? "hide" : ""}>
+ Add new wallet
</span>
</div>
<div className={accounts.apiState === "failed" ? "hide" : "separator"}></div>
<div className='right-section-content'>
{
accounts.apiState === "loading" &&
<div className='center'>
<Loader size={100} width={8}/>
</div>
}
{
accounts.apiState === "failed" &&
<div className='center'>
<div className='network-error'>
<div className='error-logo'>
<ErrorIcon />
</div>
<h3>Network Error</h3>
<div className="btn-container">
<button className="btn-submit" onClick={accounts.execute}>
Try again
</button>
</div>
</div>
</div>
}
{
(accounts.apiState === "done" && accounts.data) &&
<div className='accounts-cards'>
{ accounts.data.length &&
accounts.data.map((account: any, i) =>
<div className='card' key={i}>
<div className='card-header'>
<img src={account.imgURL} alt={account.name} />
<h4>{account.name}</h4>
</div>
<div className='card-balance'>
{account.balance.replace(/\B(?=(\d{3})+(?!\d))/g, ",")} {account.currency}
</div>
<div className='card-btn'>
<Arrow />
</div>
</div>
)
}
</div>
}
</div>
</div>
</div>
</section>
</div>
</>
);
}
Thanks for your help.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
