I have a client component in next js which uses useeffect to call an api, however its is calling twice at page load the api, i dont know why. here is the client component:
'use client';
import { useState, useEffect } from 'react';
import { useInView } from 'react-intersection-observer';
import { CaretSortIcon, CheckIcon } from '@radix-ui/react-icons';
import { cn } from '@/app/lib/utils';
import { Button } from '@/components/ui/button';
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from '@/components/ui/command';
import {
Popover,
PopoverContent,
PopoverTrigger,
} from '@/components/ui/popover';
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
import { useDebouncedCallback } from 'use-debounce';
let offset = 0;
export function ComboboxDemo({ services }: { services: string[] }) {
const [open, setOpen] = useState(false);
const [value, setValue] = useState('');
const searchParams = useSearchParams();
const initialQuery = searchParams.get('query_service') || '';
const [inputValue, setInputValue] = useState(initialQuery);
const { replace } = useRouter();
const pathname = usePathname();
const { ref, inView } = useInView();
const [data, setData] = useState<string[]>([]);
useEffect(() => {
if (inView) {
const fetchService = async () => {
const response = await fetch(
`/dashboard/inventory/api/services?offset=${offset}`,
);
const result = await response.json();
return result;
};
fetchService().then((res) => {
setData([...data, ...res]);
});
}
console.log('i fire once');
}, [inView, data]);
and here is the api endpoint:
import { db } from '@/drizzle/db';
import { evergreen } from '@/drizzle/schema';
import { ilike, sql } from 'drizzle-orm';
import { NextRequest, NextResponse } from 'next/server';
const limit = 100;
export async function GET(req: NextRequest) {
const { searchParams } = new URL(req.url);
const offset = searchParams.get('offset') || 0;
const dataPromise = db
.selectDistinct({
it_service: evergreen.it_service,
})
.from(evergreen)
.offset(sql.placeholder('offset'))
.limit(limit)
.prepare('distinct_it');
const data = await dataPromise.execute({ offset: offset });
console.log(data);
const services = data.map((item) => item.it_service);
return NextResponse.json(services, { status: 200 });
}
P.S: I am not in Strict Mode.
Another thing to note is that the API endoint is a route handler, not a server action.
It would seem you have added a dependency to the useEffect
hook that the callback updates. This will trigger the effect to run again.
useEffect(() => {
if (inView) {
const fetchService = async () => {
const response = await fetch(
`/dashboard/inventory/api/services?offset=${offset}`,
);
const result = await response.json();
return result;
};
fetchService().then((res) => {
setData([...data, ...res]); // <-- updates data state
});
}
console.log('i fire once');
}, [inView, data]); // <-- data state is dependency
Remove data
as an external dependency by using a functional state update, i.e. using an updater function that is passed the current state value in order to compute and return the next state value.
Example:
useEffect(() => {
if (inView) {
const fetchService = async () => {
const response = await fetch(
`/dashboard/inventory/api/services?offset=${offset}`,
);
const result = await response.json();
setData(data => [...data, ...result]);
// or setData(data => data.concat(...result));
};
fetchService();
}
}, [inView]);