skip to Main Content

I trying to put value in url and trying to extract value in search.js with router, but I’m getting this error:

TypeError: Cannot read properties of undefined (reading ‘q’)

  <form onSubmit={handleSubmit}>
    <input
      type="text"
      placeholder="Search..."
      value={searchValue}
      onChange={(e) => setSearchValue(e.target.value)}
      class="w-full px-4 py-2 rounded-md text-gray-900"
    />
    <button type="submit">Submit</button>
  </form>
</div>
const handleSubmit = (event) => {
  event.preventDefault(); // Prevent the default form submission
  router.push(`../search?q=${encodeURIComponent(searchValue)}`);
};

Search.js:

const Search = () => {
  const router = useRouter();
  const [searchQuery, setSearchQuery] = useState('');

  useEffect(() => {
    if (router.isReady) {
      setSearchQuery(router.query.q || '');
    }
  }, [router.isReady, router.query.q]);

  if (!router.isReady) {
    return <div>Loading...</div>; // Or some loading indicator
  }

  return (
    <div>
      <p>Search Query: {searchQuery}</p>
    </div>
  );
};

export default Search;

3

Answers


  1. It’s a bit of a React anti-pattern to store passed data into local state, just reference the data directly, e.g.:

    <p>Search Query: {router.query.q}</p>
    

    If router.query is potentially undefined then use a guard clause or Optional Chaining operator

    <p>Search Query: {router.query?.q ?? ""}</p>
    
    <p>Search Query: {(router.query && router.query.q) ?? ""}</p>
    

    If you must store it locally in state, like if you update it independently of the URL params, the solution is similar.

    const router = useRouter();
    const [searchQuery, setSearchQuery] = useState("");
    
    useEffect(() => {
      if (router.isReady) {
        setSearchQuery(router.query?.q ?? "");
        // setSearchQuery((router.query && router.query.q) ?? "");
      }
    }, [router.isReady, router.query]);
    

    Also, ensure you are using the Pages router (imported from "next/router") and not the App router (imported from "next/navigation").

    import { useRouter } from 'next/router';
    

    The Pages router should default router.query to an empty object {} and likely not require the checks mentioned above.

    query: Object – The query string parsed to an object, including
    dynamic route parameters. It will be an empty object during
    prerendering if the page doesn’t use Server-side Rendering. Defaults
    to {}

    An alternative may also be to use the useSearchParams hook.

    'use client'
    
    import { useSearchParams } from 'next/navigation';
    ...
    
    const Search = () => {
      const router = useRouter();
      const searchParams = useSearchParams();
     
      const [searchQuery, setSearchQuery] = useState(
        () => searchParams.get('q') || ""
      );
    
      useEffect(() => {
        if (router.isReady) {
          setSearchQuery(searchParams.get('q') || '');
        }
      }, [router.isReady, searchParams]);
    
      if (!router.isReady) {
        return <div>Loading...</div>; // Or some loading indicator
      }
    
      return (
        <div>
          <p>Search Query: {searchQuery}</p>
        </div>
      );
    };
    
    export default Search;
    
    Login or Signup to reply.
  2. The issue you’re encountering, TypeError: Cannot read properties of undefined (reading ‘q’), suggests that router.query is not defined or does not have the property q when you try to access it. This is a common issue with Next.js when dealing with query parameters and routing.

    Here are a few things to check and improve:

    Make sure you have imported useRouter from next/router in your Search.js file:

    import { useRouter } from ‘next/router’;

    Check the URL Construction:

    In your handleSubmit function, ensure the URL is correctly formatted. You might want to use a relative path based on the routing setup. In most cases, ../search might not be necessary if you are already on the right route. It could simply be /search:

    router.push(/search?q=${encodeURIComponent(searchValue)});

    Ensure Correct Component Rendering:

    Make sure that the Search component is correctly being rendered when navigating to /search. Sometimes issues arise if the route is not correctly matched.

    Check for router.isReady:

    Ensure router.isReady is properly used. If router.isReady is false, it may be that the router has not yet finished initializing.

    Here’s how you might adjust the Search.js component:

    import { useRouter } from 'next/router';
    import { useEffect, useState } from 'react';
    
    const Search = () => {
      const router = useRouter();
      const [searchQuery, setSearchQuery] = useState('');
    
      useEffect(() => {
        if (router.isReady) {
          const query = router.query.q || '';
          setSearchQuery(query);
        }
      }, [router.isReady, router.query.q]);
    
      if (!router.isReady) {
        return <div>Loading...</div>; // Or some loading indicator
      }
    
      return (
        <div>
          <p>Search Query: {searchQuery}</p>
        </div>
      );
    };
    
    export default Search;
    

    Server-Side Rendering Considerations:

    If your component is being server-side rendered, be aware that query parameters might not be immediately available. You might want to handle this situation by checking if router.query.q is available and ensuring the logic correctly accounts for server-side rendering.

    Debugging:

    Add some debugging statements to verify what values are present:

    useEffect(() => {
      if (router.isReady) {
        console.log('Router query:', router.query);
        setSearchQuery(router.query.q || '');
      }
    }, [router.isReady, router.query.q]);
    

    By following these steps, you should be able to resolve the issue with reading query parameters in your Next.js application.

    Login or Signup to reply.
  3. The useSearchParams hook in Next.js (introduced in Next.js 13 with the App Router) provides a more straightforward way to handle query parameters. This is especially useful when using the App Router with React Server Components. Here’s how you can use useSearchParams in your Search component:

    No change is needed here if you are using URL parameters in your navigation:

    const handleSubmit = (event) => {
      event.preventDefault();
      router.push(`/search?q=${encodeURIComponent(searchValue)}`);
    };
    

    If you are using the App Router and want to use useSearchParams, update Search.js like this:

    'use client'; // Ensure this component is client-side rendered
    
    import { useSearchParams } from 'next/navigation';
    import { useEffect, useState } from 'react';
    
    const Search = () => {
      const searchParams = useSearchParams();
      const [searchQuery, setSearchQuery] = useState('');
    
      useEffect(() => {
        const query = searchParams.get('q') || '';
        setSearchQuery(query);
      }, [searchParams]);
    
      return (
        <div>
          <p>Search Query: {searchQuery}</p>
        </div>
      );
    };
    
    export default Search;
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search