Helder Esteves

Type and hit Enter to search

JavaScript
Uncategorized
Software Architecture
React
General
Management
Next.JS
TypeScript
JavaScript
Uncategorized
Software Architecture
React
General
Management
Next.JS
TypeScript
text
React

React useEffect: 4 Tips Every Developer Should Know

Helder Esteves
Helder Esteves
May 14, 2023 4 Mins Read
126 Views
0 Comments

Let’s talk about useEffects in React Hooks! I’m going to share with you 4 tips you should have in mind when using useEffect.

Use a useEffect for a single purpose

In React Hooks, you can have multiple useEffect functions. This is a great feature because, if we analyze how to write clean code, you’ll see that functions should serve a single purpose (much like how a sentence should communicate one idea only).

Splitting useEffects into short and sweet single-purpose functions also prevents unintended executions (when using the dependency array).

For example, let’s say you have varA that is unrelated to varB, and you want to build a recursive counter based on useEffect (with setTimeout) so let’s write some bad code:

function App() {
  const [varA, setVarA] = useState(0);
  const [varB, setVarB] = useState(0);
  // Don't do this!
  useEffect(() => {
    const timeoutA = setTimeout(() => setVarA(varA + 1), 1000);
    const timeoutB = setTimeout(() => setVarB(varB + 2), 2000);

    return () => {
      clearTimeout(timeoutA);
      clearTimeout(timeoutB);
    };
  }, [varA, varB]);

  return (
    <span>
      Var A: {varA}, Var B: {varB}
    </span>
  );
}

As you can see, a single change in any of the variables varA and varB will trigger an update in both variables. This is why this hook doesn’t work properly.

Since this is a short example, you may feel that it’s obvious, however, in longer functions with more code and variables I guarantee you will miss this. So do the right thing and split your useEffect.

In this case, it should become:

function App() {
  const [varA, setVarA] = useState(0);
  const [varB, setVarB] = useState(0);
  // Correct way
  useEffect(() => {
    const timeout = setTimeout(() => setVarA(varA + 1), 1000);

    return () => clearTimeout(timeout);
  }, [varA]);

  useEffect(() => {
    const timeout = setTimeout(() => setVarB(varB + 2), 2000);

    return () => clearTimeout(timeout);
  }, [varB]);

  return (
    <span>
      Var A: {varA}, Var B: {varB}
    </span>
  );
}

Note: The code here is for example purposes only, which is meant to help you easily understand the problems of useEffect. Normally, when a variable depends on its’ previous state, the recommended way is to do setVarA(varA => varA + 1) instead.

Use custom hooks whenever you can

Let’s take the example above again. What if the variables varA and varB are completely independent?

In this case, we can simply create a custom hook to isolate each variable. This way, you get to know exactly what each function is doing to which variable.

Let’s build some custom hooks then!

function App() {
  const [varA, setVarA] = useVarA();
  const [varB, setVarB] = useVarB();

  return (
    <span>
      Var A: {varA}, Var B: {varB}
    </span>
  );
}

function useVarA() {
  const [varA, setVarA] = useState(0);

  useEffect(() => {
    const timeout = setTimeout(() => setVarA(varA + 1), 1000);

    return () => clearTimeout(timeout);
  }, [varA]);

  return [varA, setVarA];
}

function useVarB() {
  const [varB, setVarB] = useState(0);

  useEffect(() => {
    const timeout = setTimeout(() => setVarB(varB + 2), 2000);

    return () => clearTimeout(timeout);
  }, [varB]);

  return [varB, setVarB];
}

Now each variable has its’ own hook. Much more maintainable and easy to read!

Conditionally run useEffect the right way

On the topic of setTimeout, let’s take the following example:

function App() {
  const [varA, setVarA] = useState(0);

  useEffect(() => {
    const timeout = setTimeout(() => setVarA(varA + 1), 1000);

    return () => clearTimeout(timeout);
  }, [varA]);

  return <span>Var A: {varA}</span>;
}

For some reason, you want to limit the counter to a maximum of 5. There’s the correct way and the incorrect way.

Let’s take a look at the incorrect way first:

function App() {
  const [varA, setVarA] = useState(0);

  // Don't do this!
  useEffect(() => {
    let timeout;
    if (varA < 5) {
      timeout = setTimeout(() => setVarA(varA + 1), 1000);
    }

    return () => clearTimeout(timeout);
  }, [varA]);

  return <span>Var A: {varA}</span>;
}

Although this works, bear in mind that clearTimeout will run on any change of varA, while setTimeout is conditionally run.

The recommended way to run useEffect conditionally is to do a conditional return at the beginning of the function, like so:

function App() {
  const [varA, setVarA] = useState(0);

  useEffect(() => {
    if (varA >= 5) return;

    const timeout = setTimeout(() => setVarA(varA + 1), 1000);

    return () => clearTimeout(timeout);
  }, [varA]);

  return <span>Var A: {varA}</span>;
}

This is what you see Material UI using (as well as many others) and it makes sure you’re not running useEffect by mistake.

Type out every prop inside useEffect in the dependency array

If you’re using ESLint, then you’ve probably seen a warning that comes from ESLint exhaustive-deps rule.

This is crucial. When your app grows bigger and bigger, more dependencies (props) get added into each useEffect. To keep track of all of them and avoid stale closures, you should add each and every dependency into the dependency array. (Here’s the official take on this subject)

Again, on the topic of setTimeout, let’s say you want to run setTimeout only once and add on to varA, much like the previous examples.

You might be tempted to do the following incorrect example:

function App() {
  const [varA, setVarA] = useState(0);

  useEffect(() => {
    const timeout = setTimeout(() => setVarA(varA + 1), 1000);

    return () => clearTimeout(timeout);
  }, []); // Avoid this: varA is not in the dependency array!

  return <span>Var A: {varA}</span>;
}

Although this will do what you want, let’s take a moment to think, “what if your code gets bigger?”, or, “what if I want to change the code above to something else?”

In that case, you’ll want to have the variables all mapped out, as it will be much easier to test and detect problems that might arise (like stale props and closures).

The correct way should be:

function App() {
  const [varA, setVarA] = useState(0);

  useEffect(() => {
    if (varA > 0) return;

    const timeout = setTimeout(() => setVarA(varA + 1), 1000);

    return () => clearTimeout(timeout);
  }, [varA]); // Great, we have our dependency array correctly set

  return <span>Var A: {varA}</span>;
}

That’s it, folks. If you have any questions or suggestions, I’m all ears! Reply or comment below and I’ll be sure to check it out!

Tags:

Tips

Share Article

Other Articles

This is the sign you've been looking for neon signage
Previous

Mastering the Art of Software Development: Best Practices Cheat Sheet

grayscale photo of person holding glass
Next

Top 10 JavaScript Coding Challenges You Must Absolutely Know

Next
grayscale photo of person holding glass
May 17, 2023

Top 10 JavaScript Coding Challenges You Must Absolutely Know

Previews
May 11, 2023

Mastering the Art of Software Development: Best Practices Cheat Sheet

This is the sign you've been looking for neon signage

No Comment! Be the first one.

    Leave a Reply Cancel reply

    Your email address will not be published. Required fields are marked *

    Subscribe
    Stay Updated!
    Get notified when awesome content is posted. Learn more everyday.
    Most Read
    Software Architecture
    5 JS Design Patterns Every Developer Should Know
    March 10, 2023
    5 Min Read
    yellow line on gray asphalt road
    Next.JS
    5 Next.JS Tips You Never Heard About
    March 27, 2023
    4 Min Read
    blue UTP cord
    Uncategorized
    Setting up a TURN server with Node: Production-ready
    May 19, 2023
    5 Min Read
    Most Engagement
    JavaScript
    Simple Backgrounds For Websites — Cheat Sheet

    This cheat sheet will help you the next time you're choosing a background for your website....

    February 21, 2023
    3 Min Read
    Most Recent
    grayscale photo of person holding glass
    JavaScript
    Top 10 JavaScript Coding Challenges You Must Absolutely Know
    May 17, 2023
    9 Min Read
    a person pointing at a rock with writing on it
    JavaScript
    7 Best Coding Challenge Websites for 2023: Improve Your Skills and Boost Your Career
    May 20, 2023
    5 Min Read
    blue UTP cord
    Uncategorized
    Setting up a TURN server with Node: Production-ready
    May 19, 2023
    5 Min Read

    Related Posts

    brown rope on black background
    Uncategorized
    The Crash Course on Node Redis
    March 3, 2023
    4 Min Read
    Read More
    JavaScript
    The Ultimate Sith Lord Developer Guide
    April 28, 2023
    5 Min Read
    Read More
    low angle of black metal tower
    General
    Top 5 Cross-platform Frameworks to Use in 2023
    March 12, 2023
    4 Min Read
    Read More
    a computer screen with a bunch of buttons on it
    JavaScript
    JavaScript VSCode Extensions You Never Heard About
    April 25, 2023
    3 Min Read
    Read More

    Helder Esteves

    Read everything there is to know about the JavaScript development ecosystem, from programming to project management.

    Recent

    7 Best Coding Challenge Websites for 2023: Improve Your Skills and Boost Your Career
    May 20, 2023
    Setting up a TURN server with Node: Production-ready
    May 19, 2023

    Categories

    JavaScript9
    Uncategorized4
    Software Architecture2
    React2

    Contact

    Reach out to us through helder.writer@gmail.com

    © 2022, All Rights Reserved.

    Go to mobile version