简体   繁体   English

ForwardRef 警告 React-hook-forms with Material UI TextField

[英]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).我正在尝试使用带有 Material UI 输入的 react-hook-forms 构建一个表单(在这种情况下是我的 TextField 自定义变体)。 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()?你的意思是使用 React.forwardRef() 吗?

I am using react-hook-form's Controller to wrap my TextField (as suggested by the docs)我正在使用 react-hook-form 的 Controller 来包装我的 TextField(如文档所建议的那样)

Any suggestions or solutions are very welcome!非常欢迎任何建议或解决方案!

Below both the TextField component and the form where this issue occurs:在出现此问题的 TextField 组件和表单下方:

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>
    )
};

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 .尝试使用Controller的 render prop 而不是as ,因为 TextField 公开的 ref 实际上称为 inputRef,而Controller正在尝试访问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.您可以单击以下链接查看实际行为,现在使用 Controller 正确分配了 ref,当出现错误时,我们可以成功地专注于该领域以获得更好的可访问性。

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

The warning is completely right as suggested by the official docs it think you did not reach to the functional components part.该警告完全正确,正如官方文档所建议的那样,它认为您没有触及功能组件部分。 Link to the offical docs链接到官方文档

You cannot give ref to functional components as they do not have instances您不能为功能组件提供 ref,因为它们没有实例

If you want to allow people to take a ref to your function component, you can use forwardRef (possibly in conjunction with useImperativeHandle ), or you can convert the component to a class.如果你想让人们引用你的函数组件,你可以使用forwardRef (可能与useImperativeHandle结合使用),或者你可以将组件转换为一个类。

You can, however, use the ref attribute inside a function component as long as you refer to a DOM element or a class component like this:但是,只要您像这样引用 DOM 元素或类组件,就可以在函数组件中使用ref属性:

function CustomTextInput(props) {
  // textInput must be declared here so the ref can refer to it
  const textInput = useRef(null);
  
  function handleClick() {
    textInput.current.focus();
  }

  return (
    <div>
      <input
        type="text"
        ref={textInput} />
      <input
        type="button"
        value="Focus the text input"
        onClick={handleClick}
      />
    </div>
  );
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM