Search code examples
javascriptreactjsreact-nativereact-hookspassword-generator

Issue in password generator project using react


I have written a code to generate password within the specified length. I am taking length of password from the slider, but getting password with random length it didn't generate the password from the given length from slider.

React js file:

import Slider from 'rc-slider'
import 'rc-slider/assets/index.css'
import { useState } from 'react'

import passwordGif from '../../assets/gif/password.gif'
import { ReactComponent as Copy } from '../../assets/icons/copy.svg'
import { ReactComponent as Refresh } from '../../assets/icons/refresh.svg'

import './index.css'



const PasswordGenerator = () => {
  const [passwordLength, setPasswordLength] = useState(8)
  const [upperCase, setUpperCase] = useState(false)
  const [lowerCase, setLowerCase] = useState(false)
  const [number, setNumber] = useState(false)
  const [specialChar, setSpecialChar] = useState(false)
  const [password, setPassword] = useState('');

  const [strength, setStrength] = useState('')
  const [copy, setCopy] = useState('Copy');
  const [colors, setColor] = useState("#FF0000")
  //const color = colors[strength]

  const onChangePasswordLength = (value) => {
    setPasswordLength(value);
    calculateStrength()
  }

  const handleCheckBox = (event) => {
    const value = event.target.checked
    console.log('value in checkBox', value)
    switch (event.target.id) {
      case 'uppercase':
        setUpperCase(value)
        break

      case 'lowercase':
        setLowerCase(value)
        break

      case 'numbers':
        setNumber(value)
        break

      case 'special chars':
        setSpecialChar(value)
        break

      default:
        console.log('in default case')
    }
  }

  const calculateStrength = () => {
    if(password.length === 0) return;
    if (password.length >= 12) {
      setStrength('Strong');
      setColor("#12b40e");
      console.log("color", colors)
    } else if (password.length >= 8 && password.length <= 11) {
      setStrength('Medium');
      setColor("#ffa200");
    } else if (password.length >= 2 && password.length <= 7) {
      setStrength('Weak');
      setColor('#ff0000');
    }
    console.log('strength:', strength)
  }

  async function copyContent() {
    try {
      if (password.length === 0) {
        setCopy('Copy')
        setStrength('Password is empty choose the correct filter and generate')
        setTimeout(() => {
          setStrength('')
        }, 2000)
        return
      }
      await navigator.clipboard.writeText(password)
      setCopy('Copied')
      setTimeout(() => {
        setCopy('Copy')
      }, 2000)
      console.log('copy text:', copy)
    } catch (e) {
      setCopy('Failed')
    }
  }

  const generatePassword = () => {
    const password = []
    const length = passwordLength
    console.log('lenght:', length)
    const characters =
      'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_-+={[}]|<?/'

    for (let i = 0; i < length; i++) {
      const index = Math.floor(Math.random() * characters.length)
      if (upperCase && characters[index] >= 'A' && characters[index] <= 'Z') {
        password.push(characters[index])
      } else if (lowerCase && characters[index] >= 'a' && characters[index] <= 'z') {
        password.push(characters[index])
      } else if (number && characters[index] >= '0' && characters[index] <= '9') {
        password.push(characters[index])
      } else if (specialChar && characters[index] >= '!' && characters[index] <= '/') {
        password.push(characters[index])
      }
    }
    setPassword(password.join(''))
    calculateStrength()
  }

  return (
    <div className="password-wrapper">
      <div className="gif">
        <img src={passwordGif} alt="Password Gif" />
      </div>
      <div className="tac">
        <h2 className="title">PASSWORD GENERATOR</h2>
        <p className="subtitle">
          Create strong and secure passwords to keep your account safe online.
        </p>
      </div>
      <div className="password-input-wrapper">
        <div className="password-field">
          <input type="text" placeholder="Password" value={password} onChange={generatePassword} />
        </div>
        <button className="copy-btn" onClick={copyContent} value={copy}>
          <Copy /> {copy}
        </button>
      </div>
      
      <p style={{color : `${colors}`, fontWeight:'bold', marginLeft:"10px", marginTop:"15px"}} >{strength}</p>
      <div className="slider">
        <div>
          <label id="slider-label">Password Length: </label>
          <span>{passwordLength}</span>
        </div>
        <Slider
          max={30}
          min={5}
          value={passwordLength}
          onChange={onChangePasswordLength}
          className="slider-style"
        />
      </div>

      <div className="checkbox-wrapper">
        <input
          className="inputcheck"
          type="checkbox"
          name="upperCase"
          id="uppercase"
          onChange={handleCheckBox}
          checked={upperCase}
          value={upperCase}
        />
        <label> UpperCase </label>
        <input
          type="checkbox"
          name="lowerCase"
          id="lowercase"
          onChange={handleCheckBox}
          checked={lowerCase}
          value={lowerCase}
        />
        <label> Lowercase </label>
        <input
          type="checkbox"
          name="numbers"
          id="numbers"
          onChange={handleCheckBox}
          checked={number}
        />
        <label> Numbers </label>
        <input
          type="checkbox"
          name="specialChars"
          id="special chars"
          onChange={handleCheckBox}
          checked={specialChar}
        />
        <label> Special Charcters </label>
      </div>

      <button className="generate-btn" onClick={() => generatePassword()}>
        Generate Password
      </button>
    </div>
  )
}

export default PasswordGenerator

Expectation is that code should generate password with the length given by slider.


Solution

  • You are getting variable length passwords because your for loop is running n-times but you are not adding a character to the password on every iteration. For example: if for i=1 loop gave you a number but you do not want a number then you will not add it to the password, hence 1 iteration wasted. Use a do-while loop for this and break the loop whenever newpassword.length=== n

      const generatePassword = () => {
        const newpassword = [];
        const length = passwordLength;
        console.log("length:", length);
        const characters =
          "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_-+={[}]|<?/";
    
        do {
          const index = Math.floor(Math.random() * characters.length);
          if (upperCase && characters[index] >= "A" && characters[index] <= "Z") {
            newpassword.push(characters[index]);
          } else if (
            lowerCase &&
            characters[index] >= "a" &&
            characters[index] <= "z"
          ) {
            newpassword.push(characters[index]);
          } else if (
            number &&
            characters[index] >= "0" &&
            characters[index] <= "9"
          ) {
            newpassword.push(characters[index]);
          } else if (
            specialChar &&
            characters[index] >= "!" &&
            characters[index] <= "/"
          ) {
            newpassword.push(characters[index]);
          }
        }while(newpassword.length !== length)
    
        setPassword(newpassword.join(""));
        console.log(newpassword.join(""));
      };
    

    using For loop:

      const generatePassword = () => {
        const newpassword = [];
        const length = passwordLength;
        console.log("length:", length);
        const characters =
          "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_-+={[}]|<?/";
    
        for(let i=0; i<200; i++){
          const index = Math.floor(Math.random() * characters.length);
          if (upperCase && characters[index] >= "A" && characters[index] <= "Z") {
            newpassword.push(characters[index]);
          } else if (
            lowerCase &&
            characters[index] >= "a" &&
            characters[index] <= "z"
          ) {
            newpassword.push(characters[index]);
          } else if (
            number &&
            characters[index] >= "0" &&
            characters[index] <= "9"
          ) {
            newpassword.push(characters[index]);
          } else if (
            specialChar &&
            characters[index] >= "!" &&
            characters[index] <= "/"
          ) {
            newpassword.push(characters[index]);
          }
        if(newpassword.length === length) break;
        }
    
        setPassword(newpassword.join(""));
        console.log(newpassword.join(""));
      };
    

    Put calculateStrength() inside useEffect so that it runs whenever the password is updated:

    useEffect(()=>{
    calculateStrength();
    },[password]);