Password Generator || useEffect, useRef, useCallback
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:
Slider for Password Length:
To allow users to change the number of characters in the generated password, a slider was implemented using theuseState
hook. This ensures the app reacts dynamically to user input.Number and Character Checkbox:
I added a checkbox to include or exclude numbers in the password. I initialized the state usinguseState(false)
to set the default option as unchecked. This allows users to easily toggle the inclusion of numbers.Updating Input Field Values:
For managing the input field,useState
hook was used again to update the value based on user interaction.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 usedi <= {length}
to pass the value of length, which caused an error. I was unaware that the correct syntax should bei <= length
.Also, I set the variable
pass
and did not usepass +=
to append characters. Instead, I didpass = str.charAt(char)
. As a result, each iteration of the loop was overriding the value ofpass,
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
aspass +=str.charAt[char]
, instead ofpass +=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.
Subscribe to my newsletter
Read articles from Aaks directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by