(React) Why does calling 'setSize' directly within useEffect not work?

Hello. I’m going through “Full React Course 2020” on the freeCodeCamp.org YouTube channel (Full React Course 2020 - Learn Fundamentals, Hooks, Context API, React Router, Custom Hooks - YouTube), and I have a question.

Here is the source code.

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

const UseEffectCleanup = () => {
  const [size, setSize] = useState(window.innerWidth);
  const checkSize = () => setSize(window.innerWidth);
  useEffect(() => {
    window.addEventListener("resize", checkSize);
  });
  return (
    <>
      <h1>window size</h1>
      <h2>{size} PX</h2>
    </>
  );
};

export default UseEffectCleanup;

Here is my question. What is the difference between the following two?

(1) Calling checkSize, which essentially invokes setSize.

const checkSize = () => {setSize(window.innerWidth)};
useEffect(() => {
  window.addEventListener("resize", checkSize);
});

(2) Directly invoking setSize within useEffect.

useEffect(() => {
  window.addEventListener("resize", setSize(window.innerWidth);
});

The first one works and the second one doesn’t, and I was wondering why.

First remember that useEffect needs two parameters, the second one being a dependencies list, a list of things to listen for - if they change, run this. You’ve given it nothing so it will run on every render. If you want it to only run on the first render, put an empty array. If you want it to run the first time and whenever a specific variable changes, put that variable in that array.

This:

useEffect(() => {
  window.addEventListener("resize", setSize(window.innerWidth);
});

When it sets up, it has to know what that second parameter is. You’ve given it an invoked function, so it will run it as it sets up that callback. If you want it to run later (which is what you want), you need to give it a function, not the return value from that function. Traditionally, you would wrap that in an arrow function:

useEffect(() => {
  window.addEventListener("resize", () => setSize(window.innerWidth);
});

Now you are passing it a function that that listener can call when it is triggered.

Just to be clear on what an event listener is doing…

There is a process running that listens to events. What you are saying is, “if there is is an event called ‘resize’, then run this function”. So, that second parameter has to be a function. If you call the function, it is no longer the function but is the return value of that function.

Thank you so much for your reply! It makes sense now.

Now I understand that I’m getting the return value of the function if I invoke it within the second parameter of the event listener. I just want to invoke it when a ‘resize’ event occurs, thus I need to pass a function (an arrow function as you have mentioned, or a reference to a function, such as the checkSize function that I initially defined above useEffect.) That clears up quite a few confusions that I have had regarding callback functions.

1 Like