Ewaren
Ewaren

Reputation: 1763

When and why to useEffect

This may seem like a weird question, but I do not really see many use cases for useEffect in React (I am currently working on a several thousand-lines React codebase, and never used it once), and I think that there may be something I do not fully grasp.

If you are writing a functional component, what difference does it make to put your "effect" code in a useEffect hook vs. simply executing it in the body of the functional component (which is also executed on every render) ?

A typical use case would be fetching data when mounting a component : I see two approaches to this, one with useEffect and one without :

// without useEffect
const MyComponent = () => {
    [data, setData] = useState();
    if (!data) fetchDataFromAPI().then(res => setData(res));

    return(
        {data ? <div>{data}</div> : <div>Loading...</div>}
    )
}
// with useEffect
const MyComponent = () => {
    [data, setData] = useState();
    useEffect(() => {
    fetchDataFromAPI().then(res => setData(res))
    }, []);

    return(
        {data ? <div>{data}</div> : <div>Loading...</div>}
    )
}

Is there an advantage (performance-wise or other) to useEffect in such usecases ?

Upvotes: 5

Views: 1059

Answers (3)

PrakashT
PrakashT

Reputation: 901

useEffect is handling the side effect of the problem. useEffect is the combination of componentDidMount and componentDidUpdate. every initial render and whenever props updated it will be executed.

For an exmaple:

useEffect(() => {
      fetchDataFromAPI().then(res => setData(res))
}, []);

Another example:

let's assume you have multiple state variables, the component will re-render for every state values change. But We may need to run useEffect in a specific scenario, rather than executing it for each state change.

function SimpleUseEffect() {

  let [userCount, setUserCount] = useState(0);
  let [simpleCount, setSimpleCount] = useState(0);

useEffect(() => {
    alert("Component User Count Updated...");
  }, [userCount]);

  useEffect(() => {
    alert("Component Simple Count Updated");
  }, [simpleCount]);

  return (
    <div>
      <b>User Count: {userCount}</b>
      <b>Simple Count: {simpleCount}</b>
      <input type="button" onClick={() => setUserCount(userCount + 1}} value="Add Employee" />
      <input type="button" onClick={() => setSimpleCount(simpleCount + 1}} value="Update Simple Count" />
    </div>
  )
}

In the above code whenever your props request changed, fetchDataFromAPI executes and updated the response data. If you don't use useEffect, You need to automatically handle all type of side effects.

  1. Making asynchronous API calls for data

  2. Setting a subscription to an observable

  3. Manually updating the DOM element

  4. Updating global variables from inside a function

for more details see this blog https://medium.com/better-programming/https-medium-com-mayank-gupta-6-88-react-useeffect-hooks-in-action-2da971cfe83f

Upvotes: 0

mgarcia
mgarcia

Reputation: 6325

The thing is, useEffect is not executed on every render.

To see this more clearly, let's suppose that your component MyComponent is being rendered by a parent component (let's call it ParentComponent) and it receives a prop from that parent component that can change from a user action.

ParentComponent

const ParentComponent = () => {
    const [ counter, setCounter ] = useState(0);
    const onButtonClicked = () => setCounter(counter + 1);

    return (
        <>
            <button onClick={onButtonClicked}>Click me!</button>
            <MyComponent counter={counter} />
        </>
    );
}

And your MyComponent (slightly modified to read and use counter prop):

const MyComponent = ({ counter }) => {
    [data, setData] = useState();

    useEffect(() => {
        fetchDataFromAPI().then(res => setData(res))
    }, []);

    return(
        <div>
            <div>{counter}</div>
            {data ? <div>{data}</div> : <div>Loading...</div>}
        </div>
    )
}

Now, when the component MyComponent is mounted for the first time, the fetch operation will be performed. If later the user clicks on the button and the counter is increased, the useEffect will not be executed (but the MyComponent function will be called in order to update due to counter having changed)!

If you don't use useEffect, when the user clicks on the button, the fetch operation will be executed again, since the counter prop has changed and the render method of MyComponent is executed.

Upvotes: 0

marzelin
marzelin

Reputation: 11600

I. Cleanup

What if your component gets destroyed before the fetch is completed? You get an error.

useEffect gives you an easy way to cleanup in handler's return value.

II. Reactions to prop change.

What if you have a userId passed in a props that you use to fetch data. Without useEffect you'll have to duplicate userId in the state to be able to tell if it changed so that you can fetch the new data.

Upvotes: 1

Related Questions