0

I'm trying to implement a hook (useFetch(route, dependencies=[])) which will be used to fetch data and return a scalar value, loading, which is used to determine if the user is currently fetching new data.

This works as expected on the initial render (as the default state for loading is true).

The problem occurs on every subsequent render. Due to the loading state being currently set to false (after the initial render), the loading state will remain false for an extra render cycle before it is updated to true at the top of the useEffect().

How can I prevent that extra loading=false render cycle, so the component I use this useFetch() in is immediately aware that data is currently being fetched and that I can display the '...loading' text in the render() (without having to render twice to get to this point).

useFetch

function useFetch(route, successHandler, dependencies = []) {
    const [loading, setLoading] = useState(true);
    useEffect(() => {
        setLoading(true);
        fetch(route,
            {
                method: 'POST',
                body: JSON.stringify({}),
                headers: {"Content-Type": "application/json"}
            })
            .then(res => res.json())
            .then(
                () => {
                  // Set state data from component here 
                  successHandler();
                }, 
                () => {/*...*/})
            .finally(() => {
                setLoading(false);
            });
    }, dependencies);
    return loading;
}

Component

function Component(){
  const [toggle, setToggle] = useState(false);
  const [options, setOptions] = useState({});
  const loading = useFetch('endpoint', 
    () => setOptions({foo: 'bar'}), 
    [toggle]);

  return <div>
    <button onClick={()=>{setToggle(!toggle)}}>Trigger useFetch()</button>
    {loading ? '...loading': options.foo}
  </div>;
}

Any help or design suggestions would be appreciated,

1 Answer 1

2

Your code has two mistakes:

const loading = useFetch('endpoint', 
    () => setOptions({foo: 'bar'}), 
    [toggle]);
  1. The dependency array should be the variables that are used in the useEffect, in your case it can be [route]
  2. [toggle] as a dependency parameter will send [false] or [true], depending on your state variable. Which won't be under control of the useEffect of the hook you created.

Check out this example, it might help you understand better:

Edit friendly-ives-xt5k0

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

4 Comments

I wouldn't want toggle to be under the control of the useEffect, i intend for it to be used to trigger that useEffect instead. Does that go against the hooks rules?
Yep. It does. You need to use the variables that are used in the useEffect hook or an empty array.
Check the example I added.
Thank you for the fiddle, checking it out now

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.