I want to execute a function only after a state has been set.
My code is as below
export function Details() {
let params = useParams();
const certificateData = useContext(DataContext) //fetching data from the database on load
const [individualData,setIndividualData] = useState([])
const [tokenID,setTokenID] = useState('')
const setIndividualDetails = async () => {
setIndividualData(certificateData.find(uploadedFile => uploadedFile.QRurl === params.QRurl))
}
useEffect(() => {
const fetchTokenID = async () => {
await setIndividualDetails()
try{
const {data} = await axios({
method: "GET",
url: `https://api.nftport.xyz/v0/mints/${individualData.transaction_hash}?chain=polygon`,
headers: {
'Content-Type': 'application/json',
Authorization: '4d86xxxxxx-929e-xxxx'
}
});
// setTokenID(token_id)
console.log(data.token_id)
}catch (error) {
console.log(error)
}
}
fetchTokenID()
},[certificateData])
return (
<div>
{individualData && (<h2>{individualData.transaction_hash}</h2>)}
</div>
)}
In the above code my axios call to the api requires the paramater individualData.transaction_hash which is undefined in the intitial render and throws the undefined error
My context (DataContext) that sets the value of certificateData gets its value only on the second render, meanwhile axiox request has been called on the first render
How do I ensure the axios request runs only after individualData has got its value? Please help
I tried a lot of async await but it does not seem to work.
Edit after answer changed my useEffect to below and took setIndividualDetails outside
useEffect(() => {
if (certificateData.length === 0) {
return;
}
setIndividualDetails()
if (!individualData || individualData.length === 0)
return;
fetchTokenID()
}, [certificateData, individualData]);
CodePudding user response:
Why not just condition it?
const fetchTokenID = async () => {
...
};
useEffect(() => {
// Make sure `individualData` has members.
if (individualData.length === 0) {
return;
}
// If above condition is not met, run the function as intended.
fetchTokenID();
}, [certificateData, individualData]);
Here we make the useEffect
also "watch" the value of individualData
by including it in the dependency array.
And then basically saying only run the useEffect
contents if the individualData
array contains values.
I'm assuming you'd want to run fetchTokenID
only a single time, in which case we should also handle that, since this useEffect
will run every time the value of individualData
changes.
This can be handled either via useRef
that would determine if the function has already ran.
Example:
const hasFetchedRef = useRef(false);
const fetchTokenID = async () => {
...
};
useEffect(() => {
// Make sure the function doesn't run more than a single time.
if (hasFetchedRef.current) {
return;
}
// Make sure `individualData` is not `null`/`undefined` and that it has members.
if (!individualData || individualData.length === 0) {
return;
}
// If above conditions are not met, run the function as intended.
fetchTokenID();
}, [certificateData, individualData]);
Alternatively, maybe we could rely on the value of tokenID
if we set it in the fetchTokenID
.
Example:
const fetchTokenID = async () => {
...
setTokenID(someValue);
...
};
useEffect(() => {
// Make sure the function doesn't run more than a single time.
if (!tokenID) {
return;
}
// Make sure `individualData` is not `null`/`undefined` and that it has members.
if (!individualData || individualData.length === 0) {
return;
}
// If above conditions are not met, run the function as intended.
fetchTokenID();
}, [certificateData, individualData]);