skip to Main Content

I’m working on a Next.js project where I have a component that fetches data using axios and the useEffect hook. The data is fetched based on the router.query object to enable dynamic routing. However, I’m facing an issue where changing the link doesn’t update the fetched data, and it returns the old data from the previous router.query.

Here’s a simplified version of my code:

import { useEffect, useState } from 'react';
import { useRouter } from 'next/router';
import axios from 'axios';

const MyComponent = () => {
  const router = useRouter();
  const [data, setData] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await axios.get(`/api/data/${router.query.id}`);
        setData(response.data);
      } catch (error) {
        console.error('Error fetching data:', error);
      }
    };

    fetchData();

    return () => {
      // Optional cleanup function if needed
    };
  }, [router.query]);

  return (
    <div>
      {/* Render data here */}
    </div>
  );
};

export default MyComponent;

In an attempt to resolve the issue, I tried using AbortController to cancel the previous fetch when the useEffect runs with the new router.query values. However, this approach didn’t seem to solve the problem, and I’m still getting outdated data from the previous query.

Here is the attempted code:

import { useEffect, useState } from 'react';
import { useRouter } from 'next/router';
import axios from 'axios';

const MyComponent = () => {
  const router = useRouter();
  const [data, setData] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      const abortController = new AbortController();

      try {
        const response = await axios.get(`/api/data/${router.query.id}`, {
          signal: abortController.signal,
        });
        setData(response.data);
      } catch (error) {
        if (error.name === 'AbortError') {
          console.log('Fetch aborted');
        } else {
          console.error('Error fetching data:', error);
        }
      }
    };

    fetchData();

    return () => {
      abortController.abort();
    };
  }, [router.query]);

  return (
    <div>
      {/* Render data here */}
    </div>
  );
};

export default MyComponent;

2

Answers


  1. I think the issue you are facing is that when the route changes, the useEffect runs again but the new data is not immediately available (as it needs to be loaded first). Therefore, that first re-render triggered by the route change still has the previous data in the data state.

    I would keep the AbortController (as it can be useful if you navigate across multiple pages too fast for any request to finish), but you should also reset the data state before requesting the new data:

    import { useEffect, useState } from 'react';
    import { useRouter } from 'next/router';
    import axios from 'axios';
    
    const MyComponent = () => {
      const router = useRouter();
      const [data, setData] = useState(null);
    
      useEffect(() => {
        const fetchData = async () => {
          setData(null) // <= HERE!
    
          const abortController = new AbortController();
    
          try {
            const response = await axios.get(`/api/data/${router.query.id}`, {
              signal: abortController.signal,
            });
    
            setData(response.data);
          } catch (error) {
            if (error.name === 'AbortError') {
              console.log('Fetch aborted');
            } else {
              console.error('Error fetching data:', error);
            }
          }
        };
    
        fetchData();
    
        return () => {
          abortController.abort();
        };
      }, [router.query]);
    
      return (
        <div>
          {/* Render data here */}
        </div>
      );
    };
    
    export default MyComponent;
    
    Login or Signup to reply.
  2. Try this way

    import { useEffect, useState, useCallback } from 'react';
    import { useRouter } from 'next/router';
    import axios from 'axios';
    
    const MyComponent = () => {
      const router = useRouter();
      const [data, setData] = useState(null);
    
      const fetchData = useCallback(async () => {
        try {
          const response = await axios.get(`/api/data/${router.query.id}`);
          return response.data;
        } catch (error) {
          console.error('Error fetching data:', error);
        }
       }, []);
        
       useEffect(() => {
         fetchData().then(data => {
           setData(data);
         });
       }, [fetchData, router.query]);
    
        return (
          <div>
            {/* Render data here */}
          </div>
        );
    };
    
    export default MyComponent;
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search