Is there a way to clear a MUI TextField Input from the form.restart() method in React-Final-Form and capture the event?

1k Views Asked by At

The form.restart() in my reset button resets all fields states and values as per my understanding of this Final-Form. The method fires and resets all fields in my form and I can capture the event in the autocomplete, but I am unable to capture the clear event in the textfield - I have a state (not related to the value of the field) I need tor reset.

My form reset button

<Button
  type={"button"}
  disabled={submitting || pristine}
  variant={"outlined"}
  onClick={() => {
    form.getRegisteredFields().forEach((field) => form.resetFieldState(field));
    form.restart();
    if (clearActionHandler) {
      clearActionHandler();
    }
    setFormSubmittedOnce(false);
  }}
>
  Clear
</Button>;

My textfieldadapter

const [shrink, setShrink] = useState < boolean > false;
const countCharacters: (
  e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
) => boolean = (e) => {
  setCount(e.target.value.length);
  return maxCharacterCount === 0 || e.target.value.length < maxCharacterCount;
};

return (
  <TextField
    {...input}
    {...rest}
    onChange={(e) => {
      if (countCharacters(e)) {
        input.onChange(e);
      }
    }}
    value={input.value}
    onBlur={(e) => {
      !input.value && input.onBlur(e);
      !input.value && setShrink(false);
    }}
    error={meta.error && meta.touched}
    helperText={
      meta.touched ? (
        <React.Fragment>
          {maxCharacterCount > 0 ? (
            <React.Fragment>
              <Typography variant={"body1"} textAlign={"end"}>
                {count}/{maxCharacterCount}
              </Typography>
              <br />
            </React.Fragment>
          ) : null}{" "}
          {meta.error}
        </React.Fragment>
      ) : maxCharacterCount > 0 ? (
        <React.Fragment>
          <Typography variant={"body1"} textAlign={"end"}>
            {count}/{maxCharacterCount}
          </Typography>
          <br />
        </React.Fragment>
      ) : (
        ""
      )
    }
    placeholder={placeholder}
    fullWidth={true}
    margin={"dense"}
    multiline={multiline > 1}
    rows={multiline}
    inputProps={inputProps}
    InputProps={{
      startAdornment: (
        <InputAdornment position={"start"} sx={{ width: "24px" }}>
          {startAdornment}
        </InputAdornment>
      ),
    }}
    InputLabelProps={{
      shrink: shrink,
    }}
    onFocus={() => setShrink(true)}
    sx={{
      "& .MuiInputLabel-root:not(.MuiInputLabel-shrink)": {
        transform: "translate(50px, 17px)",
      },
    }}
  />
);

Versions of packages: "@mui/material": "^5.11.1", "react": "^18.2.0", "react-final-form": "^6.5.9"


I have tried to capture the onChange event with bluring all elements before the reset method is called, that doesn't call the textfield onblur method. I am just not sure how to clear it away.

1

There are 1 best solutions below

1
adsy On

Make sure that the field in question has an entry in initialValues and that that entry is initialised to an empty string. This is a long shot, so take everything I say as theory, but it looks like what would happen if the initial value was not defined or was explicitly set to undefined.

That would mean when you reset the form, the value prop of the TextField would be set to undefined. This is not a valid controlled value of a TextField, so the behavior of MUI could well be to switch to uncontrolled mode, where the value is kept internally and not actually controlled by the value prop any more. It depends on the library what happens next as this is generally unexpected and leads to non-deterministic behavior, but it's likely that MUI just keeps the previous controlled value around internally, or it's sitting in a transient DOM state on the element itself since value is judged to be no longer the source of truth and so it was never written to the actual DOM element. When the user types again, onChange would be called, the state in your form set to a string, and it would become controlled again. Base react <input> gives warnings in the console when you transition like this but MUI might not.

This might explain why it does not capture the transition to the original form state.

Note null is usually not allowed either but there's some final form magic that protects you from that one.

I verified MUI does this on a codesandbox (excuse the old class style I just used an old TextField sandbox to build on). If my long shot is correct, the problem is more related to a core issue between your state consistency and the mui lib and less about final form.

The fix would be to make sure the field has an entry in initialValues set to a string. You could optionally put in guards to check for undefined and use '' instead when that's the case.

Take note that '' is actually still falsey so !input.value would evaluate to true when it's empty string still. Not sure you care, but something to keep in mind.