自定义 React 组件在与 react-hook-form 一起使用时不显示输入长度



具有以下Textarea组件,它是为了可重用而构建的,它是一个具有maxlength道具的基本文本区域,其中可以指定最大输入长度,它还显示当前输入长度,格式为current input length/max length

它作为一个单独的组件工作得很好,问题是当它必须与react hook表单一起使用时,当前输入长度没有更新。

这是文本区域组件:

import React, { useState, useEffect } from 'react';
import useTextareaController from './use-textarea-controller';
export interface TexareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
maxLength?: number;
id: string;
}
export const Textarea = React.forwardRef(
(
{ id, maxLength = 200, ...props }: TexareaProps,
ref: React.ForwardedRef<HTMLTextAreaElement>
) => {
const { textareaRefSetter } = useTextareaController(ref);
const [count, setCount] = useState(0);
useEffect(() => {
const refElement = document.getElementById(id) as HTMLTextAreaElement;
if (refElement) {
setCount(refElement.value.length);
}
}, [id]);
return (
<div>
<div>
{count}/{maxLength}
</div>
<textarea
id={id}
ref={textareaRefSetter}
onChange={(event) => setCount(event.target.value.length)}
maxLength={maxLength}
{...props}
></textarea>
</div>
);
}
);
export default Textarea;

它使用了另一个钩子中的useTextareaController,这里是代码:

import React, { useRef } from 'react';
/**
* Utility function which registers and then removes event listeners for specified elements.
* @param el Reference of element for which to register event
* @param eventType native event type
* @param onEventCallback callback to be bound to event
*/
/**
* Controls the appearance of Button / Icon that is assigned to the reference,
* using the visibility property.
*
* This implementation has been made in the effort of
* keeping the input uncontrolled whenever it is possible.
*
* @See https://react-hook-form.com/api/useform/register/
* @param forwardedInputRef forwarded reference to be set by this hook
* @param disabled clear button / icon won't appear if it is false
* @returns referenceSetter function to assign the inner input element to the forwarded reference
* and the reference of the clear button / icon
*/
export const useTextareaController = (
forwardedInputRef: React.ForwardedRef<HTMLTextAreaElement>
) => {
const innerInputRef = useRef<HTMLTextAreaElement>();
// Both the inner reference and the forwarded reference should be set
const textareaRefSetter = (el: HTMLTextAreaElement) => {
innerInputRef.current = el;
if (!forwardedInputRef) return;
if (typeof forwardedInputRef === 'function') {
forwardedInputRef(el);
}
if (typeof forwardedInputRef === 'object') {
forwardedInputRef.current = el;
}
};
return { textareaRefSetter };
};
export default useTextareaController;

下面是一个模态组件,它内部有Textarea,并使用react钩子形式进行验证:

import { yupResolver } from '@hookform/resolvers/yup';
import { useForm } from 'react-hook-form';
import * as yup from 'yup';
import { Modal, Textarea } from '../shared/ui-components';
const schema = yup.object().shape({
description: yup.string().required()
});
export interface Task {
description: string;
}
export interface MyModalProps {
title: string;
open: boolean;
toggle: () => void;
}
export function MyModal({ title, open, toggle }: MyModalProps) {
const emptyTask = { description: '' };
const { handleSubmit, reset, register } = useForm({
resolver: yupResolver(schema)
});
const onSubmit = (data: Task) => {
// send a POST request
toggle();
reset(emptyTask);
};
return (
<Modal title={title} open={open} onClose={toggle} onSubmit={handleSubmit(onSubmit)}>
<div>
<Textarea id='my-textarea' {...register('description')} />
</div>
</Modal>
);
}
export default MyModal;

有没有一种方法可以使当前输入长度与反作用钩子形式结合使用?

我想这些更改必须在Textarea组件中完成。

react hook表单提供了自己的onChange处理程序,它将作为props的一部分传递,当您将props扩展到文本区域道具中时,这可能会破坏您的自定义处理程序。

相反,您应该从props中提取onChange,并定义自己的onChange回调,如果它被传入,就会调用它,而不是将它扩展到您的props中。

export const Textarea = React.forwardRef(
(
{ id, maxLength = 200, onChange, ...props }: TexareaProps,
ref: React.ForwardedRef<HTMLTextAreaElement>
) => {
const { textareaRefSetter } = useTextareaController(ref);
const [count, setCount] = useState(0);
useEffect(() => {
const refElement = document.getElementById(id) as HTMLTextAreaElement;
if (refElement) {
setCount(refElement.value.length);
}
}, [id]);
const onChangeHandler = useCallback(
(event) => {
setCount(event.target.value.length);
onChange?.(event);
},
[setCount, onChange]
);
return (
<div>
<div>
{count}/{maxLength}
</div>
<textarea
id={id}
ref={textareaRefSetter}
onChange={onChangeHandler}
maxLength={maxLength}
{...props}
></textarea>
</div>
);
}
);

最新更新