Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it wrong to fetch an API without using the useEffect Hook?

I've been doing it this way but some colleges told me that I should use the useEffect Hook instead. The problem is that I don't see the benefit of that approach and I think that my approach is cleaner.

import React, { useState, useEffect } from "react";

const fetchTheApi = () =>
  new Promise(res => setTimeout(() => res({ title: "Title fetched" }), 3000));

const UseEffectlessComponent = () => {
  const [data, setData] = useState();
  !data && fetchTheApi().then(newData => setData(newData));
  return <h1>{data ? data.title : "No title"}</h1>;
};

const UseEffectComponent = () => {
  const [data, setData] = useState();
  useEffect(() => {
    fetchTheApi().then(newData => setData(newData));
  }, []);
  return <h1>{data ? data.title : "No title"}</h1>;
};

const MyComponent = () => (
  <div>
    <UseEffectlessComponent />
    <UseEffectComponent />
  </div>
);

Edit based on responses:

I changed the code to re render, like this:

import React, { useState, useEffect } from 'react';

const fetchTheApi = (origin) => {
    console.log('called from ' + origin);
    return new Promise((res) =>
        setTimeout(() => res({ title: 'Title fetched' }), 3000)
    );
};

const UseEffectlessComponent = () => {
    const [data, setData] = useState();
    !data &&
        fetchTheApi('UseEffectlessComponent').then((newData) => setData(newData));
    return <h1>{data ? data.title : 'No title'}</h1>;
};

const UseEffectComponent = () => {
    const [data, setData] = useState();
    useEffect(() => {
        fetchTheApi('UseEffectComponent').then((newData) => setData(newData));
    }, []);
    return <h1>{data ? data.title : 'No title'}</h1>;
};

const MyComponent = () => {
    const [counter, setCounter] = useState(0);
    counter < 3 && setTimeout(() => setCounter(counter + 1), 1000);
    return (
        <div>
            <p>counter is: {counter}</p>
            <UseEffectlessComponent />
            <UseEffectComponent />
        </div>
    );
};

In the console I got:

called from UseEffectlessComponent called from UseEffectComponent called from UseEffectlessComponent called from UseEffectlessComponent called from UseEffectlessComponent

So, I finally found the benefit to that approach. I've got some code to change... Thanks a lot for the answers!

like image 368
elpopisencio Avatar asked May 20 '19 13:05

elpopisencio


People also ask

Why do we need useEffect hook?

The useEffect Hook allows you to perform side effects in your components. Some examples of side effects are: fetching data, directly updating the DOM, and timers.

Should you fetch data in useEffect?

From what I've been reading (blog posts, react official docs) it is generally recommended that data fetching function should be defined inside the useEffect() , although there could be some edge cases.

Should I use useState or useEffect?

The useState hook is used for storing variables that are part of your application's state and will change as the user interacts with your website. The useEffect hook allows components to react to lifecycle events such as mounting to the DOM, re-rendering, and unmounting.

How do you fetch data from an API with React hooks?

Put the fetchData function above in the useEffect hook and call it, like so: useEffect(() => { const url = "https://api.adviceslip.com/advice"; const fetchData = async () => { try { const response = await fetch(url); const json = await response. json(); console. log(json); } catch (error) { console.


1 Answers

How you've written it does work, kind of. You're saying "If the fetch fails and the component re-renders, then try again, else don't". Personally I think that is an unreliable system - depending on a re-render to try again, and can easily have unintended side-effects:

  • What if your data is falsy? What if it fails (which you didn't handle). In this case it will keep trying to re-fetch.

  • What if the parent renders 3 times in a row (a very common situation). In that case your fetch will happen 3 times before the first fetch is complete.

So with that in mind you actually need more careful checks to ensure you code doesn't have unexpected consequences by not using useEffect. Also if your fetch wanted to re-fetch on prop changes your solution also doesn't work.

like image 82
Dominic Avatar answered Oct 27 '22 10:10

Dominic