running a useEffect after a click even if the url trigger isn't changed

116 Views Asked by At

I'm following the nice article "How to fetch data with React Hooks?", in particular the section "ERROR HANDLING WITH REACT HOOKS".

There we have a useEffect that depends on the url where we fetch data from.

    fetchData();
  }, [url]);

The url is set on a submit from an input, specifying the query string.

  <form
    onSubmit={() =>
      setUrl(`http://hn.algolia.com/api/v1/search?query=${query}`)
    }
  >

My problem is that, even if the author defines

The error is just another state initialized with a state hook

and he implements the try/catch block for error handling and he finally concludes:

The error state is reset every time the hook runs again. That's useful because after a failed request the user may want to try it again which should reset the error.

actually I see that the useEffect to fetch data depends only on the url, that is the query string, hence, if the user doesn't change the query string, he can't try again. This can be useful especially to try after an error, but even more in general. How to achieve this goal? I've tried

    fetchData();
  }, [url, isError]);

but it gets stuck in a loop of updates...

2

There are 2 best solutions below

0
On

My solution for this problem was

  1. to add another hook state (representing the need to repeat the data fetch):

    const [repeat, setRepeat] = useState(false);
    
  2. to make the useEffect dependent on it (also adding a cleanup):

      fetchData();
      return function cleanup() {
        setRepeat(false);
      };
    }, [url, repeat]);
    
  3. and finally adding a check in the submit to force the data fetch even if the query string wasn't changed:

    <form onSubmit={event => {
      const before = url;
      setUrl(algoliaUrl(query));
      if  (url === before)
      {
        setRepeat(true);
      }
      event.preventDefault(); }}>
    
0
On

I'd add a boolean in state to track when to execute the fetchData()

const [loadContent, setLoadContent] = useState(true);

useEffect would look like this

useEffect(() => {
  const fetchData = async () => {
    setLoadContent(false) // Resetting the flag
    setIsError(false);
    setIsLoading(true);
    try {
      const result = await axios(`${url}${query}`);
      setData(result.data);

    } catch (error) {
      setIsError(true);
    }

    setIsLoading(false);
  };
  loadContent && fetchData() // Fetch data only if the flag is true 
});

And finally form element like this

<form onSubmit={event => {
      setLoadContent(true);
      event.preventDefault();
}}>

Codesandbox link