1

In React, Autocomplete Chrome values don't trigger onChange event immediately. Thus it causes a collision of MUI TextField Label and actual values, during initial loading of page. How can I resolve this issue ?

enter image description here

Trying numerous methods, InputLabelProps shrink on Value does not work either. https://stackoverflow.com/a/65070465/15435022

<StyledTextField
    fullWidth
    id="email"
    label="Email"
    size="medium"
    value={emailAddress}
    onChange={(e) => setEmailAddress(e.target.value)}
    error={emailError}
    InputLabelProps={{
      shrink: emailAddress != null && emailAddress != "" ? true : false,
    }}
    helperText={emailError ? "Please enter a valid email" : "Required"}
 />

Trying this solution also gives issues: when doing const theme = createTheme({

Github resource: https://github.com/mui/material-ui/issues/14427#issuecomment-466054906

enter image description here

isherwood
  • 58,414
  • 16
  • 114
  • 157
mattsmith5
  • 540
  • 4
  • 29
  • 67
  • I think you can use `onInput` in `inputProps` instead of `onChange`. But I'm not sure it's working for you. – Pluto Aug 03 '23 at 18:56

2 Answers2

2

Personally, I don't think the juice is worth the squeeze for this vs just disabling shrink or auto-complete for the log-in, but no one asked my opinion, so...

From what I understand, the reason this is more painful than we'd like is because it was originally intended as a security feature to prevent password theft via auto-complete, but once Chrome (et al.) removed the restrictions, React's de-duping mechanism took over. There are some slightly more hack-y work-arounds than what I'm going to suggest, but you can decide which you like.

To side-step this, you can add a handler to each input's onAnimationStart event, check if the animationName is "mui-auto-fill", and then check if the input has a -webkit-autofill pseudo class, to see if the browser has auto-filled the field. You'll also want to handle the "mui-auto-fill-cancel" case for scenarios where the form is auto-filled and the user clears the values (to reset shrink.)

For example:

const [passwordHasValue, setPasswordHasValue] = React.useState(false);

// For re-usability, I made this a function that accepts a useState set function
// and returns a handler for each input to use, since you have at least two 
// TextFields to deal with.
const makeAnimationStartHandler = (stateSetter) => (e) => {
  const autofilled = !!e.target?.matches("*:-webkit-autofill");
  if (e.animationName === "mui-auto-fill") {
    stateSetter(autofilled);
  }

  if (e.animationName === "mui-auto-fill-cancel") {
    stateSetter(autofilled);
  }
};
...

<TextField
  type="password"
  id="password"
  inputProps={{
    onAnimationStart: makeAnimationStartHandler(setPasswordHasValue)
  }}
  InputLabelProps={{
    shrink: passwordHasValue
  }}
  label="Password"
  value={password}
  onChange={(e) => {
    setPassword(e.target.value);
    ...
  }}
/>

The result on load should appear as:

example

Update with cancel -- allows user to clear out the form field, after load with auto-fill, and the label is reset:

example with reset field

FYI: I made my makeAnimationStartHandler a function that accepts a React.useState() setter as a param and returns a handler that actually does the work because your example has two fields and I wanted to 1) reduce code and 2) allow you to still handle the manual entry use-case for each field separately, if desired.

Working Example: https://z3vxm7.csb.app/
Working CodeSandbox: https://codesandbox.io/s/autofill-and-mui-label-shrink-z3vxm7?file=/Demo.tsx

Steve
  • 11,596
  • 7
  • 39
  • 53
  • for some reason its not working, the label always Stays on Top, even when textbox is empty on mine – mattsmith5 Aug 04 '23 at 08:13
  • @mattsmith5 Ah I missed the cancel animation in my example -- I've updated my answer. – Steve Aug 04 '23 at 15:39
  • 1
    @SteveGomez Also there would be usefull to shrink it on focus as it is default behaviour. I am gonna fork it with focus and blur handler. https://codesandbox.io/s/autofill-and-mui-label-shrink-forked-kd9699 – ruckie Aug 25 '23 at 13:35
1

Thanks for this Steve. Works great. I wrote a component for my project based on your answer:

import {TextField} from "@mui/material";
import {useCallback, useState} from "react";

const AutoFillAwareTextField = ({onChange, inputProps, InputLabelProps, ...rest}) => {

    const [fieldHasValue, setFieldHasValue] = useState(false)
    const makeAnimationStartHandler = (stateSetter) => (e) => {
        const autofilled = !!e.target?.matches("*:-webkit-autofill");
        if (e.animationName === "mui-auto-fill") {
            stateSetter(autofilled);
        }

        if (e.animationName === "mui-auto-fill-cancel") {
            stateSetter(autofilled);
        }
    }

    const _onChange = useCallback((e) => {
        onChange(e.target.value);
        setFieldHasValue(e.target.value !== "")
    }, [onChange])

    return <TextField
        inputProps={{
            onAnimationStart: makeAnimationStartHandler(setFieldHasValue),
            ...inputProps
        }}
        InputLabelProps={{
            shrink: fieldHasValue,
            ...InputLabelProps
        }}
        onChange={_onChange}
        {...rest}
    />
}
export default AutoFillAwareTextField
Morten Moe
  • 11
  • 1