I have two different API end points to fetch data from. The second endpoint requires a query parameter that comes from the first endpoints response.
I am using useSWR hook with axios inside a custom hook which looks like-
import axios from '@/lib/axios'
import { useEffect, useState } from 'react'
import useSWR from 'swr'
export const useSubjects = ({ id, path } = {}) => {
/**
* Fetcher Function for SWR
*/
const fetchSubjects = async url => {
return await axios
.get(url)
.then(res => res.data)
.catch(error => {
if (error.response.status !== 409) throw error
})
}
const fetchThumbnail = async (url, path) => {
var thumbImages = []
await path.map(async pathdata => {
// console.log(url + pathdata)
await axios
.get(url + pathdata, {
responseType: 'blob',
})
.then(res => thumbImages.push(res.data))
.catch(error => {
throw error
})
})
return thumbImages
}
/**
* swr hook for fetching subjects
*/
const {
data: subjects,
error: SubjectError,
mutate: mutateSubject,
} = useSWR(
id > 0 ? `api/get-subject-branch/${id}` : 'api/get-subject-branches',
fetchSubjects,
)
/* swr hook for fetching thumbnails */
const {
data: thumbnail,
error: thumbnailError,
mutate: mutateThumbnail,
} = useSWR(
path ? ['api/get-subject-thumbnail?thumbnail_url=', path] : null,
fetchThumbnail,
)
return {
subjects,
thumbnail,
mutateSubject,
mutateThumbnail,
}
}
Using the hook:
const AdminCourses = () => {
const [thumbnailPaths, setThumbnailPaths] = useState([])
const [thumbBlob, setThumbBlob] = useState([])
// const [thumbnail, setThumbnail] = useState()
// Subjects fetched from API using subjects hook
const { subjects, thumbnail, mutateSubject, mutateThumbnail } = useSubjects(
{
path: thumbnailPaths,
},
)
useEffect(() => {
if (subjects) {
setThumbnailPaths([])
subjects?.data?.map(sub => {
setThumbnailPaths(thumbnailPaths => [
...thumbnailPaths,
sub.thumbnail_url,
])
})
}
}, [subjects])
useEffect(() => {
if (thumbnail) {
setThumbBlob([])
thumbnail?.map(thumbnailBlobData => {
console.log(thumbnailBlobData)
var imgData = []
imgData.push(thumbnailBlobData)
setThumbBlob(thumbBlob => [
...thumbBlob,
URL.createObjectURL(new Blob(imgData, { type: 'Image/*' })),
])
})
}
// console.log('calling effect to update data')
return () => {
thumbBlob.map(data => URL.revokeObjectURL(data))
}
}, [subjects, thumbnail])
useEffect(() => {
if (thumbnailPaths?.length != 0) {
console.log("calling thumbnail swr", thumbnailPaths, thumbBlob);
mutateThumbnail()
}
}, [thumbnailPaths])
return (<>
{subjects?.data?.map((data, ind) => (
<div key={'subject' + ind}>
<hr className="bg-black h-101 mt-8" />
{/* Start of Subjects / Courses */}
<div className="flex justify-between items-center lg:flex-col md:flex-row sm:flex-col my-4">
{/* subject thumbnail */}
<div className="w-32 pl-8 lg:mt-8 md:mt-0 sm:mt-8">
<Image
src={
thumbBlob?.length != 0
? thumbBlob[ind]
: '/image/blog-2.jpg'
}
width={5706}
height={3840}
layout="responsive"
objectFit="cover"
className="rounded-xl"
/>
</div>
<div className="flex flex-col lg:mt-8 md:mt-0 sm:mt-8">
{/* subject title */}
{/* <h1 className="text-xl font-bold">
{data.subject_name}
</h1> */}
<CourseNav
DataName={data.subject_name}
className="relative -left-6 bg-btn-color font-bold rounded-md shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none"
/>
</div>
{/* subject description */}
<h4 className="text-sm font-bold self-center w-2/5 lg:text-base lg:w-3/4 lg:mt-8 md:mt-0 md:w-2/5 md:text-sm sm:text-base sm:w-3/4 sm:mt-8">
{data.subject_description}
</h4>
<div className="flex justify-between items-center lg:my-8 md:my-0 sm:my-8">
{/* Edit Button */}
<div className="cursor-pointer hover:scale-110 transform transition-all duration-500 pr-8">
<FontAwesomeIcon icon={faPen} size="2x" />
</div>
{/* delete video btn */}
<div className="cursor-pointer hover:scale-110 transform transition-all duration-500">
<DeleteIcon />
</div>
</div>
</div>
{/* End of Subjects / Courses */}
</div>
))}
</>
)}
The given custom hook in the first snippet is used in the component given in the second snippet.
The Data:
There are two types of data fetched using these end points
First, an array of json objects containing subjects
Second, array of blob of images - thumbnail
The subjects
array contains thumbnail_url
in each element.
The images are fetched using those thumbnail_url
The Problem:
The thumbnail_url paths are stored in the state hook thumbnailPaths
which is an array of all thumbnail_urls
Again, thumbnailPaths
is passed as an object parameter in useSubjects hook.
Initial call to useSubjects hook is definitely an empty array of thumbnailPaths
. But later when the thumbnailPaths
array is populated with data it is expected to call the useSubjects
hook with the updated thumbnailPaths
array.
Shouldn't it re-render the component and call useSubjects again so that the second useSWR in useSubject custom hook gets called again?
What is happening in this case is. Despite thumbnailPaths
being populated the second useSWR hook is not being called unless mutateSubject()
is being called which I am calling using a button click.
The call to mutateThumbnail
seems to have no effect whatsoever.
I am getting thumbnail images only on calling mutateSubjects
What I want is to get "All" data in one go without having to reload or press any button.