0

I made a search panel component, searchPanel.tsx. On it, a button grabs the details from the form (Flight From, Flight To, Flight Date, and some other data), then sends that to a new page.

Here's the searchPanel.tsx:

'use client';    
import "./searchPanel.css";
import React, { useState } from "react";
import { useRouter } from "next/navigation";

export default function SearchPanel() {
  const router = useRouter();
  const [isUsingFlightNum, setIsUsingFlightNum] = useState(false);
  const [locFromValue, setLocFromValue] = useState('');
  const [locToValue, setLocToValue] = useState('');
  const [flightNumValue, setFlightNumValue] = useState('');
  const [flightDateValue, setFlightDateValue] = useState('');


  const locFromChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setLocFromValue(e.target.value);
  };

  const locToChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setLocToValue(e.target.value);
  };

  const flightNumChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setFlightNumValue(e.target.value);
  };

  const flightDateChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setFlightDateValue(e.target.value);
  };

  const handleSubmit = async (formData: FormData) => {
    const flightFrom = formData.get('flightFrom')?.toString() || "";
    const flightTo = formData.get('flightTo')?.toString() || "";
    const flightNumber = formData.get('flightNumber')?.toString() || "";
    const flightDate = formData.get('flightDate')?.toString() || "";

    if (flightDate !== "" && (flightFrom !== "" && flightTo !== "" || flightNumber !== "")) {
      const params = new URLSearchParams();

      params.set('is_using_flight_number', isUsingFlightNum.toString());
      params.set('flight_from', flightFrom);
      params.set('flight_to', flightTo);
      params.set('flight_num', flightNumber);
      params.set('flight_date', flightDate);

      router.push(`/flight/choose?${params.toString()}`)
    } else {
      alert("One or more required fields are missing.");
    }
  }

  return (
    <div>
      Front end code here
    </div>
  );
}

That routes to another page, flight/choose/page.tsx. This page now takes the parameters from the search panel, and uses those as parameters for an API call:

export default async function SelectFlight( {searchParams} : MyPageProps ) {
  // const [selectedFlightCardId, setSelectedFlightCardId] = useState(-1);
  // ^^ Code that I need
  const params = await searchParams;
  const isUsingFlightNum = params!.is_using_flight_number || "";
  const flightFrom = params!.flight_from || "";
  const flightTo = params!.flight_to || "";
  const flightNumber = params!.flight_num || "";
  const flightDate = params!.flight_date || "";

  const url = new URL(`${BASE_API_URL}/api/flights`);
  const options = {
    method: "GET",
  };

  url.searchParams.append('is_using_flight_number', isUsingFlightNum.toString());
  url.searchParams.append('flight_from', flightFrom);
  url.searchParams.append('flight_to', flightTo);
  url.searchParams.append('flight_num', flightNumber);
  url.searchParams.append('flight_date', flightDate);
  const response = await fetch(url, options);
  const result = await response.json();
  const flightData = result.data;

  const handleClick = (event) => {
    const clickedId = event.target.id;
    console.log(`clickedId`, clickedId);
  }

  return (
    <div>
      Front end code here
    </div>
  );
}

This works, until I add "use client" at the very top, which I need for a useState to update a clicked <div> styling and store the ID.

Then the page just repeatedly calls the fetch code, until I kill the project or navigate elsewhere.

I have no idea what to do. Removing my code and simply adding something like a console.log("help") would see help printed repeatedly in the console as well.

5
  • 1
    Try wrapping the fetch logic in useEffect with the params as dependency Commented Sep 5 at 8:49
  • 2
    You're hitting an infinite loop because you're mixing a server component (async function) with client behavior (useState, "use client"). Once you add "use client", the function becomes a client component, but await fetch(...) runs on every render. Move the fetch logic into a useEffect inside a proper client component and store the result in state, that will stop the repeated calls. Commented Sep 5 at 9:47
  • @KabileeshG Tried it, thanks. However now the page doesn't update when the data is received. I'm guessing cause I declared a let flightData: any[] = [] at the very top of the code. Commented Sep 5 at 10:15
  • @GianlucaTiengo, did you mean make another component for the page.tsx (say, results.tsx), and put the entire fetch function in there, and then call said component in the page.tsx? Sorry if I'm not understanding it right. Commented Sep 5 at 10:17
  • 1
    @zack_falcon I guess you're updating the page based on the received data- that you are storing in flightData. If so, you need to maintain a state(useState) for flightData and set the data once its received. To render a client component it needs to be in the life-cycle hook. So maintain a state for that data from which the component gets built Commented Sep 6 at 11:11

2 Answers 2

-1

When you use client, it means the component is running in the browser. In this case, you can't directly await fetch because every time it renders, it fetches again and goes into a loop. You have to put fetch in useEffect and store the result in state. When you don't use client, it means the component is server-side. There, you can directly await fetch because it only runs once on the server and doesn't loop again.

Sign up to request clarification or add additional context in comments.

Comments

-1

Your fetch operation in client component must be wrapped in useEffect to prevent infinite loops. When fetch runs directly in the component body of a client component, each render triggers another fetch, which updates state and causes another render.

So move the fetch to useEffect with searchParams as a dependency, ensuring it only executes when parameters change. Also I will change BASE_API_URL as NEXT_PUBLIC_BASE_API_URL for clientside env variable access.

Here's the corrected flight/choose/page.tsx

'use client';
import { useState, useEffect } from 'react';

interface MyPageProps {
  searchParams: Promise<{
    // your properties
  }>;
}

export default function SelectFlight({ searchParams }: MyPageProps) {
  const [flightData, setFlightData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    const fetchFlightData = async () => {
      try {
        setLoading(true);
        const params = await searchParams;
        
        const isUsingFlightNum = params?.is_using_flight_number || "";
        const flightFrom = params?.flight_from || "";
        const flightTo = params?.flight_to || "";
        const flightNumber = params?.flight_num || "";
        const flightDate = params?.flight_date || "";

        const url = new URL(`${process.env.NEXT_PUBLIC_BASE_API_URL}/api/flights`);
        
        url.searchParams.append('is_using_flight_number', isUsingFlightNum);
        url.searchParams.append('flight_from', flightFrom);
        url.searchParams.append('flight_to', flightTo);
        url.searchParams.append('flight_num', flightNumber);
        url.searchParams.append('flight_date', flightDate);

        const response = await fetch(url);
        
        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`);
        }

        const result = await response.json();
        setFlightData(result.data);
      } catch (err) {
        setError(err instanceof Error ? err.message : 'An error occurred');
      } finally {
        setLoading(false);
      }
    };

    fetchFlightData();
  }, [searchParams]);

  const handleClick = (event) => {
    const clickedId = event.target.id;
    console.log(`clickedId`, clickedId);
  }

  if (loading) return <div>Loading flights...</div>;
  if (error) return <div>Error: {error}</div>;

  return (
    <div>
      {/* Render flight data with clickable elements */}
      {flightData && (
        <div>
          {/* Your code */}
        </div>
      )}
    </div>
  );
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.