Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ForwardRef warning React-hook-forms with Material UI TextField

I am trying to build a form with react-hook-forms with Material UI's inputs (my custom variant of TextField in this case). Although the form seems to work completely fine, it triggers a warning message in the console when rendering the form.

Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?

I am using react-hook-form's Controller to wrap my TextField (as suggested by the docs)

Any suggestions or solutions are very welcome!

Below both the TextField component and the form where this issue occurs:

Component TextField

const TextField = props => {
    const {
        icon,
        disabled,
        errors,
        helperText,
        id,
        label,
        value,
        name,
        required,
        ...rest
      } = props;

    const classes = useFieldStyles();
    
    return (
        <MuiTextField 
            {...rest}
            name={name}
            label={label}
            value={value || ''}
            required={required}
            disabled={disabled}
            helperText={helperText}
            error={errors}
            variant="outlined" 
            margin="normal" 
            color="primary"
            InputProps={{
                startAdornment: icon,
                classes: {
                    notchedOutline: classes.outline,
                },
            }}
            InputLabelProps={{
                className: classes.inputLabel,
            }}
        />
    )
};

TextField.propTypes = {
    icon: PropTypes.node,
    disabled: PropTypes.bool,
    label: PropTypes.string,
    id: PropTypes.string,
    value: PropTypes.any,
    required: PropTypes.bool,
    helperText: PropTypes.string,
};

export default TextField;

Component LoginForm

const LoginForm = () => {
    const { handleSubmit, errors, control } = useForm();
    const onSubmit = values => console.log(values);

    return (
        <form onSubmit={handleSubmit(onSubmit)}>
            <Typography variant="h5" color="primary" gutterBottom>
                Login
            </Typography>

            <Box py={3} height="100%" display="flex" flexDirection="column">
                <Controller
                    as={TextField}
                    label="Username"
                    name="username"
                    control={control}
                    errors={errors}
                    required
                />

                <Controller
                    as={TextField}
                    label="Password"
                    type="password"
                    name="password"
                    control={control}
                    errors={errors}
                    required
                />

                <Link>
                    Forgot your password?
                </Link>
            </Box>

            <Button variant="contained" color="primary" fullWidth type="submit">
                Submit
            </Button>
        </form>
    )
};
like image 722
R-deBruijn Avatar asked Nov 11 '20 12:11

R-deBruijn


1 Answers

Try to use Controller's render prop instead of as, because TextField's exposed ref is actually called inputRef, while Controller is trying to access ref.

import React, { useState } from "react";
import ReactDOM from "react-dom";
import { useForm, Controller } from "react-hook-form";
import Header from "./Header";
import { TextField, ThemeProvider, createMuiTheme } from "@material-ui/core";
import "react-datepicker/dist/react-datepicker.css";

import "./styles.css";
import ButtonsResult from "./ButtonsResult";

let renderCount = 0;

const theme = createMuiTheme({
  palette: {
    type: "dark"
  }
});

const defaultValues = {
  TextField: "",
  TextField1: ""
};

function App() {
  const { handleSubmit, reset, control } = useForm({ defaultValues });
  const [data, setData] = useState(null);
  renderCount++;

  return (
    <ThemeProvider theme={theme}>
      <form onSubmit={handleSubmit((data) => setData(data))} className="form">
        <Header renderCount={renderCount} />
        <section>
          <label>MUI TextField</label>
          <Controller
            render={(props) => (
              <TextField
                value={props.value}
                onChange={props.onChange}
                inputRef={props.ref}
              />
            )}
            name="TextField"
            control={control}
            rules={{ required: true }}
          />
        </section>

        <section>
          <label>MUI TextField</label>
          <Controller
            render={(props) => (
              <TextField
                value={props.value}
                onChange={props.onChange}
                inputRef={props.ref}
              />
            )}
            name="TextField1"
            control={control}
            rules={{ required: true }}
          />
        </section>

        <ButtonsResult {...{ data, reset, defaultValues }} />
      </form>
    </ThemeProvider>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

you can click the following link for actual behavior, now with ref assigned properly with Controller, we can successfully focus on the field when there is an error for better accessibility.

https://codesandbox.io/s/react-hook-form-focus-74ecu

like image 126
Bill Avatar answered Nov 11 '22 21:11

Bill