Created
February 10, 2021 10:11
-
-
Save rtm7777/b6bcb746c375cb52502e46e882c6a9d0 to your computer and use it in GitHub Desktop.
Formik Password input with disabled autocomplete
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
useCallback, | |
useEffect, | |
useRef, | |
useState, | |
} from 'react' | |
import PropTypes from 'prop-types' | |
import clsx from 'clsx' | |
import { useField, ErrorMessage } from 'formik' | |
const maskCode = '•' | |
const PasswordField = ({ labelProps, inputProps, name }) => { | |
const id = `id-${name}` | |
const { label, required } = labelProps | |
const placeholder = inputProps.placeholder || 'enter value' | |
const inputRef = useRef(null) | |
const range = useRef(0) | |
const cursor = useRef(0) | |
const [displayValue, setDisplayValue] = useState('') | |
const [{ value = '', ...field }, , { setValue }] = useField(name) | |
const handleInputChange = useCallback( | |
({ target: { value: newValue, selectionStart } }) => { | |
cursor.current = selectionStart | |
const isIncrease = newValue.length > value.length | |
const insertLength = newValue.length - value.length + range.current | |
const newChange = newValue.substr(selectionStart - insertLength, insertLength) | |
const valueArray = Array.from(value) | |
if (isIncrease || (range.current && insertLength > 0)) { | |
valueArray.splice(selectionStart - insertLength, range.current, ...Array.from(newChange)) | |
} else { | |
valueArray.splice(selectionStart, Math.abs(insertLength) + range.current) | |
} | |
setValue(valueArray.join('')) | |
setDisplayValue(maskCode.repeat(valueArray.length)) | |
}, | |
[value, setValue], | |
) | |
const handleEvent = useCallback(({ target: { selectionStart, selectionEnd } }) => { | |
range.current = selectionEnd - selectionStart | |
}, []) | |
useEffect(() => { | |
inputRef.current.selectionStart = cursor.current | |
inputRef.current.selectionEnd = cursor.current | |
}, [value]) | |
return ( | |
<> | |
{label && ( | |
<label htmlFor={id}> | |
<span className={clsx({ required })}>{label}</span> | |
<ErrorMessage name={name} render={msg => <span className="form-error-msg">{msg}</span>} /> | |
</label> | |
)} | |
<input | |
id={id} | |
ref={inputRef} | |
type="text" | |
autoComplete="off" | |
autoCorrect="off" | |
autoCapitalize="off" | |
spellCheck="false" | |
placeholder={placeholder} | |
{...field} | |
{...inputProps} | |
value={displayValue} | |
onChange={handleInputChange} | |
onKeyDown={handleEvent} | |
onCut={handleEvent} | |
onPaste={handleEvent} | |
/> | |
</> | |
) | |
} | |
PasswordField.defaultProps = { | |
inputProps: {}, | |
labelProps: {}, | |
} | |
PasswordField.propTypes = { | |
inputProps: PropTypes.shape({ | |
placeholder: PropTypes.string, | |
}), | |
labelProps: PropTypes.shape({ | |
label: PropTypes.oneOfType([PropTypes.string, PropTypes.element]), | |
required: PropTypes.bool, | |
}), | |
name: PropTypes.string.isRequired, | |
} | |
export default PasswordField |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment