React Hooks: useEffect, useRef, and useCallback

Omkar KastureOmkar Kasture
5 min read

In last two blog we learned what are react components, props and what react hooks are. In this blog we will interact with three useful hooks namely useEffect, useRef, and useCallback with project building simultaneously.

I learned this concepts from Hitesh Choudhary (Chai with Code). Click on YouTube - chai with react for full video.

To learn the use of these hooks we will build a password generator, so let’s get started!

useEffect

Read for details: https://react.dev/reference/react/useEffect

useEffect in React is used when you need to run some code after your component renders. It's perfect for things like:

  • Fetching data when the component appears on the screen.

  • Updating the DOM manually (like changing the title of the page).

  • Setting up timers or intervals, or subscribing to events.

Without useEffect, React wouldn’t know when or how to handle actions that should happen outside the normal rendering process.

useEffect(() => {
  // Code for the side effect
}, [dependencies]);
  • Runs after render: By default, useEffect runs after every render (both initial and updates).

  • Dependencies array: If you pass an array of dependencies, the effect runs only when those dependencies change. If it's empty, the effect runs only once when the component mounts.

useCallback

For Details Visit: https://react.dev/reference/react/useCallback

The useCallback hook in React is used to memoize a function, meaning it returns a cached version of the function that only changes if the dependencies you pass to it change.

It's useful for preventing unnecessary re-renders or re-creations of functions, especially when passing callbacks to child components.

const memoizedCallback = useCallback(
  () => {
    // Your code here
  },
  [dependency1, dependency2]
);
  • Optimization: Helps optimize performance by memoizing a function.

  • Dependencies: The function will be recreated only if one of the dependencies changes.

  • Use Cases: Ideal for passing functions to components that rely on reference equality (like useEffect or React.memo).

Memoize (not memorize): In programming, memoization is an optimization technique where the result of a function call is stored so that the same result can be returned if the function is called again with the same arguments, without recalculating.

In the case of React's useCallback, it "memoizes" the function, meaning it keeps a cached version of that function and only updates it when its dependencies change. This avoids unnecessary function re-creations on every render.

Its not necessary to use useCallback every time, if the optimization is needed then only use this hook.

useRef

The useRef hook in React is a powerful tool for accessing and interacting with DOM elements or storing mutable values which are not needed for re-rendering.

  • Accessing DOM Elements: You can use useRef to directly reference a DOM element, allowing you to manipulate it directly (like focusing an input field).

  • Storing Mutable Values: It can hold any mutable value (like a variable) that persists across renders without causing re-renders when changed.

const myRef = useRef(initialValue);

Example: Accessing a DOM Element

import React, { useRef } from 'react';
function MyComponent() {
  const inputRef = useRef(null);
  const focusInput = () => {
    inputRef.current.focus(); // Directly accesses the input element and focuses it
  };

  return (
    <div>
      <input ref={inputRef} type="text" />
      <button onClick={focusInput}>Focus Input</button>
    </div>
  );
}

Example: Storing Mutable Value

import React, { useRef } from 'react';
function Counter() {
  const countRef = useRef(0);
  const increment = () => {
    countRef.current += 1; // Updates the mutable value
    console.log(countRef.current); // Logs the updated count
  };

  return (
    <div>
      <button onClick={increment}>Increment</button>
    </div>
  );
}

Project- Password Generator

Code for Project : GitHub/password-generator

Initial Work: create react project, configure tailwind, and clean autogenerated code.

The final project would be like this:

  • Its a random password generator.

  • slide bar is used for changing length of password.

  • Check box for including number and special characters in password

  • Copy button to copy password, and highlight copied password

useState:

We can see, we need to store length of password, is number allowed, is character allowed, and finally the the password generated. we use states for all of these.

const [length, setLength] = useState(8);
const [numberInclude, setNumberInclude] = useState(false);
const [charInclude, setCharInclude] = useState(false);
const [Password, setPassword] = useState("");

function to generate random password:

const generatePassword = ()=>{
    let pass="";
    let str= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    if(numberInclude) str+="1234567890";
    if(charInclude) str+="`~!@#$%^&*()";

    for(let i=0; i<length; i++){
      let idx= Math.floor(Math.random()*str.length);
      pass += str.charAt(idx)
    }
    console.log(pass);
  }

useCallback:

We need to call the random password generator function whenever any of the conditions (i.e., length, inclusion of numbers, or special characters) change. Using useCallback is an ideal solution for this.

const generatePassword = useCallback(() => {
  let pass = "";
  let str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
  if (numberInclude) str += "1234567890";
  if (charInclude) str += "`~!@#$%^&*()";

  for (let i = 0; i < length; i++) {
    let idx = Math.floor(Math.random() * str.length);
    pass += str.charAt(idx);
  }
  setPassword(pass);
}, [length, numberInclude, charInclude, setPassword]);

This ensures that generatePassword will only be recreated when the length, numberInclude, charInclude, or setPassword values change, optimizing the function's performance. otherwise it returns the same password on each re-render.

useEffect:

Now we need to call generatePassword function on first render as well as whenever the length, numberInclude, or charInclude changes, hence we have to use useEffect hook.

useEffect(() => {
    generatePassword();
  }, [length, numberInclude, charInclude, generatePassword]);

It ensure that generatePassword is executed whenever any of the specified conditions change, which keeps the generated password in sync with the current settings.

useRef:

Now after coping the generated password, we want to highlight copied password. Hence we need to access input element, so we will use useRef hook.

const passwordRef = useRef(null);

Function of copy password to clipboard is also optimized using useCallback hook

const copyPasswordToClip = useCallback(()=>{
    passwordRef.current?.select();
    passwordRef.current?.setSelectionRange(0,101); 
    window.navigator.clipboard.writeText(password);
  },[password])
   <input
   type="text"
   value={password}
   className="outline-none w-full py-1 px-3"
   placeholder="Password"
   readOnly
   ref={passwordRef}
   />
   <button onClick={copyPasswordToClip} className="outline-none bg-blue-700 text-white px-3 py-0.5 shrink-0">
   Copy
   </button>

In this blog, we explored the useCallback, useRef, and useEffect hooks and their practical applications in real projects. In the next blog, we'll dive into other hooks to expand our knowledge further.

Happy learning!

9
Subscribe to my newsletter

Read articles from Omkar Kasture directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Omkar Kasture
Omkar Kasture

MERN Stack Developer, Machine learning & Deep Learning