Skip to content

Instantly share code, notes, and snippets.

@rtm7777
Created February 10, 2021 10:11
Show Gist options
  • Save rtm7777/b6bcb746c375cb52502e46e882c6a9d0 to your computer and use it in GitHub Desktop.
Save rtm7777/b6bcb746c375cb52502e46e882c6a9d0 to your computer and use it in GitHub Desktop.
Formik Password input with disabled autocomplete
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