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

Posted

技术标签:

【中文标题】ForwardRef 警告 React-hook-forms with Material UI TextField【英文标题】:ForwardRef warning React-hook-forms with Material UI TextField 【发布时间】:2021-02-23 10:54:39 【问题描述】:

我正在尝试使用带有 Material UI 输入的 react-hook-forms 构建一个表单(在这种情况下,我是 TextField 的自定义变体)。尽管表单似乎工作得很好,但在呈现表单时会在控制台中触发警告消息。

警告:函数组件不能被赋予 refs。尝试去 访问此 ref 将失败。你的意思是使用 React.forwardRef() 吗?

我正在使用 react-hook-form 的控制器来包装我的 TextField(按照文档的建议)

非常欢迎任何建议或解决方案!

在 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;

组件登录表单

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

【问题讨论】:

【参考方案1】:

尝试使用Controller的render prop而不是as,因为TextField的暴露引用实际上称为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);

您可以点击以下链接查看实际行为,现在通过控制器正确分配 ref,我们可以在出现错误时成功聚焦该字段以获得更好的可访问性。

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

【讨论】:

谢谢比尔,我后来也采用了这种方法,这确实可以防止 Ref.我还发现以前版本的 react-hook-form (v 6.0.8) 在使用 'as' 道具时没有这种行为 是的,没错。我们在 6.10.0 中引入了额外的 ref 属性以改进焦点管理。 github.com/react-hook-form/react-hook-form/blob/master/…【参考方案2】:

警告完全正确,正如官方文档所建议的那样,它认为您没有触及功能组件部分。 Link to the offical docs

您不能将 ref 赋予功能组件,因为它们没有实例

如果你想让人们对你的函数组件进行引用,你可以使用forwardRef(可能与useImperativeHandle一起使用),或者你可以将组件转换为一个类。

但是,只要您像这样引用 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>
  );


【讨论】:

以上是关于ForwardRef 警告 React-hook-forms with Material UI TextField的主要内容,如果未能解决你的问题,请参考以下文章

React:警告:不能给函数组件提供参考。尝试访问此 ref 将失败。你的意思是使用 React.forwardRef() 吗?

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

如何在 React Native 中使用 React.forwardRef()

React-Hook详解

React-Hook详解

React-Hook详解