Password Generator || useEffect, useRef, useCallback

AaksAaks
6 min read

It’s the ninth day of my React learning journey, and until now, I've focused on mastering components one by one. While I completed a simple project previously, this time I'm diving into a more (comparatively) complex endeavor with a password generator. This project challenges me to apply the concepts I've learned so far and push my understanding of React hooks and optimization techniques.

The Idea:

I decided to create a password generator that automatically generates secure passwords. The app features a slider that allows users to determine the length of the password, as well as options to include numbers and special characters. Additionally, there’s a convenient button to copy the generated password directly to the clipboard.

Implementation:

  1. Slider for Password Length:
    To allow users to change the number of characters in the generated password, a slider was implemented using the useState hook. This ensures the app reacts dynamically to user input.

  2. Number and Character Checkbox:
    I added a checkbox to include or exclude numbers in the password. I initialized the state using useState(false) to set the default option as unchecked. This allows users to easily toggle the inclusion of numbers.

  3. Updating Input Field Values:
    For managing the input field, useState hook was used again to update the value based on user interaction.

  4. Copying the Password:
    I was able to successfully implement the functionality to copy the generated password, keep reading to find out more about how I tackled this feature

const [length, setLength] = useState(8)
const [numAllowed, setNumAllowed] = useState(false)
const [charAllowed, setCharAllowed] = useState(false)
const[password, setPassword] = useState('')

Implementing the Password Generation Feature

To start, we need to implement the password-generating feature. For this, I created a function called const passwordGenerator = () => {} that handles the logic for generating the password.

This function is designed to be called whenever a user checks or unchecks the character options or adjusts the number slider. Now there must be some solution to make this process optimized and the solution was useCallback hook.
useCallback is a React Hook that **lets you cache a function definition between re-renders.**This allows us to memoize the function so that it only gets recreated when its dependencies change, enhancing the app's performance.

How to Use useCallback

useCallback(fn, dependencies)

In this case, we'll include numberAllowed , charAllowed and length as dependencies. This means that if either the number option or character option or the length changes, the passwordGenerator function will run again. The dependencies array ensures that the function is only recreated when necessary.

Implementation

First, we need to import the hook at the top of our file:

import { useCallback } from 'react';

Next, we define our passwordGenerator function using useCallback:

const passwordGenerator = useCallback(() => {
  let pass = ""
  let str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
  if(numAllowed) str += "0123456789"
  if(charAllowed) str += "!@#$%^&*()"

  for(let i=1; i<=length; i++ ){
    const char = Math.floor(Math.random()* str.length + 1)
    pass += str.charAt(char)
  }
  setPassword(pass)
}, [length, numAllowed, charAllowed])

Error Encountered During Implementation:

  • In the for loop, I initially used i <= {length} to pass the value of length, which caused an error. I was unaware that the correct syntax should be i <= length.

  • Also, I set the variable pass and did not use pass += to append characters. Instead, I did pass = str.charAt(char). As a result, each iteration of the loop was overriding the value of pass, causing the password to consist of only one character instead of multiple characters.

  • In the same line of code, I was attempting to pass the char as pass +=str.charAt[char], instead of pass +=str.charAt(char)

After making these adjustments, the function was fully operational.


Setting Up the Elements

Now, we will create the following elements: an input field for the password, a button to copy the password, a length slider, and checkboxes to allow or disallow numbers and special characters.

Input field for password:

 <input
         type="text"
         value={password}
         placeholder='password'
        readOnly
        />

Length slider:
The onChange event will trigger when the slider value changes, passing the event to update the length state using the setLength function.

<input
          type="range"
          min = {5}
          max = {30}
          value={length}
          className='cursor-pointer'
          onChange={(e) => setLength(e.target.value)}
        />
          <label>Length:{length}</label>

Number Checkbox:
When the checkbox is checked or unchecked, it will update the numAllowed state using the setNumAllowed function.

<input 
          type="checkbox"
          Checked = {numAllowed}
          onChange={() => {setNumAllowed((prev) => !prev);
          }}/>

Character Checkbox:
When the checkbox is checked or unchecked, it will update the charAllowed state using the setCharAllowed function.

<input 
          type="checkbox"
          Checked= {charAllowed}
          onChange={() => {
            setCharAllowed((prev) => !prev);
          }}
           />

Generating the Password

Now we need to generate the password. However, simply calling the password generation function caused an error, resulting in a blank screen due to too many renders.

To resolve this, we can use a method that triggers the password generation on a button click, rather than calling it directly. Additionally, we’ll utilize the useEffect hook, which allows us to synchronize a component with an external system.

The useEffect hook requires a callback function and a dependency array to determine when the effect should run. Here’s how to set it up:

useEffect(() => {
    // Password generation logic will go here
}, []); // Empty dependency array means this runs once after the initial render

Comparing useState and useEffect Dependencies

I want to highlight the differences between the dependencies of useState and useEffect. This comparison focuses on our password generator application, rather than being an exhaustive overview of these hooks. While there are more nuances to explore, understanding their roles here has deepened my grasp of managing state and handling side effects effectively.

  • useCallback Dependencies:

    • Purpose: Memoizes a function to prevent unnecessary re-creations.

    • Dependency Array: If any dependency changes, a new function reference is returned; otherwise, the cached function is used.

  • useEffect Dependencies:

    • Purpose:The useEffect hook is used to invoke code when a component mounts or when a lifecycle event is triggered in React.

    • Dependency Array: If any dependency changes, the effect runs again; an empty array means the effect runs only once after the initial render.


Implementing Copy Functionality

When you click the copy button, the generated password should be copied. However, there’s no link between the input and the button, so we’ll use the useRef hook for this.

The useRef hook allows us to create a reference to any element.

First, we create passRef like this:

const passRef = useRef(null);

Then, we need to pass this reference to the input field:

ref={passRef}

Implementing onClick:

To copy the password on the button click, we need to add an onClick event to the button:

onClick = {copyPassToClipboard}

Now, we have to create the copyPassToClipboard method. We can use the useCallback hook for this:

const copyPassToClipboard = useCallback(() => {
  window.navigator.clipboard.writeText(password);
}, [password]);

Optimization on copying:

However, when copying, we need to ensure the text is selected first; otherwise, the user won’t realize that it worked. For that, we can use passRef to select the text:

passRef.current?.select();

Here’s what the function will look like:

const copyPassToClipboard = useCallback(() => 
  {
    passRef.current?.select();
    passRef.current?.setSelectionRange(0, 30);
    window.navigator.clipboard.writeText(password);

  },[password])

In this project, we developed a password generator using React hooks to create a smooth and interactive experience. Key features include a slider for adjusting password length, checkboxes for including numbers and special characters, and a simple copying mechanism with useRef. We optimized our functions with useCallback to minimize re-renders and improve performance.

To be honest, I found this project a bit challenging. Until now, I was learning components individually, but this project represented a significant step for me. I understand the concepts, but I know there's a lot of room for improvement in my retention and application of them. I still need more practice, and I hope that with future projects, I’ll become more comfortable using these hooks and optimizations.

0
Subscribe to my newsletter

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

Written by

Aaks
Aaks