带有自定义组件和自定义挂钩的 forwardRef

forwardRef with custom component and custom hook

编辑:为提高可读性而进行的小改动。

我是新来的反应者,我可能已经陷入困境,但无论如何我都会继续..

我有一个登录组件,当用户单击提交时输入元素失去焦点 and/or 时,我想在其中向用户提供反馈。

我知道我用 useState 实现了类似的行为,但为了教育我正在尝试 useRef

我在 LoginForm.js 中收到 inputRef 未定义读取的类型错误。所以当 validateInput 被调用时 inputRef 没有被赋值。谁能帮我弄清楚为什么会这样,是否有解决办法?

LoginForm.js:

import useInput from '../../hooks/use-input';
import Input from '../../UI/Input/Input';

 const LoginForm = () => {
  const { inputRef, isValid } = useInput(value =>
    value.includes('@')
  );

  return <Input ref={inputRef} />;
};

use-input.js(自定义挂钩):

const useInput = validateInput => {
  const inputRef = useRef();
  const isValid = validateInput(inputRef.current.value);
  return {
    inputRef,
    isValid,
  };
};

Input.js(自定义元素组件):

const Input = forwardRef((props, ref) => {
  return <input ref={ref} {...props.input}></input>;
});

我看到的一个问题是,在 Input 组件中,您使用的是 props.input,为什么?

const Input = forwardRef((props, ref) => {
  return <input ref={ref} {...props}></input>;
});

您希望将发送的 props 分配给组件。

接下来,您正在做 value.includes('@'),但您确定 value 不是 undefined 吗?

const { inputRef, isValid } = useInput(value =>
    value && value.includes('@')
  );

这将消除该错误的可能性。


解决 inputRef is undefined 的问题并不难解决。

之后,您将面临另一个问题。您正在使用 useRef(不受控制)这一事实不会导致重新呈现,因此,如果您更新输入内容,isValid 将不会更新其值。

Keep in mind that useRef doesn’t notify you when its content changes. Mutating the .current property doesn’t cause a re-render. (React Docs)

这是个人笔记,但我发现不受控制的组件通常很难 maintain/scale/...,而且 ref 通常不打算做这种事情。 (是的,是的,你有 react-form-hook,它提供了一种使用不受控制的组件创建表单的方法,是的,它是高效的)。

同时,在我进一步研究这个问题的同时,我可以使用 useState.

为您提供解决方案
const useInput = (validationRule, initialValue='') => {
  const [value, setValue] = useState(initialValue)

  const onChange = (e) => setValue(e.target.value)

  const isValid = validationRule && validationRule(value)

  return {
    inputProps: {
      value,
      onChange
    },
    isValid
  }
}

所以,在这里我们有一个函数,它有 2 个参数,validationRuleinitialValue(这是可选的,如果没有提供,将默认为文本)。

我们正在做基本的 value / onChange 事情,然后我们将这 2 个作为 inputProps 返回。此外,我们只是调用 validationRule(之前,我们检查它是否存在并将其作为参数发送)。

使用方法:

export default function SomeForm() {
  const { inputProps, isValid } = useInput((value) => value.includes('@'));
  
  return <Input {...inputProps}/>;
}

以下部分我强烈反对。 这很糟糕,但目前,使用 refs 实现它的唯一方法是使用 useReducer 强制更新 onChange.

例如:

const useInput = (validationRule) => {
  const [, forceUpdate] = useReducer((p) => !p, true);
  const inputRef = useRef();

  const onChange = () => forceUpdate();

  const isValid = validationRule && validationRule(inputRef.current?.value);

  return {
    inputRef,
    isValid,
    onChange
  };
};

然后,用作:

export default function SomeForm() {
  const { inputRef, onChange, isValid } = useInput((value) =>
    value && value.includes("@")
  );
  console.log(isValid);

  return <Input ref={inputRef} onChange={onChange} />;
}