gpt4 book ai didi

Error focus not working reliably for conditionally rendered fields - React Hook Form(错误焦点不能可靠地用于有条件呈现的字段-反应挂钩表单)

转载 作者:bug小助手 更新时间:2023-10-25 16:58:09 35 4
gpt4 key购买 nike



In my react hook form component, I have a select component for selecting a payment option from one of two options: Mpesa Buy Goods and Mpesa Paybill. Both of these fields are rendered conditionally, so they're only displayed once the user selects one of either. Because of this, the conventional RHF register method alone doesn't work, so a custom ref is necessary.

在我的Reaction钩子表单组件中,我有一个选择组件,用于从以下两个选项之一中选择付款选项:MPesa Buy Goods和MPesa PayBill。这两个字段都是有条件地呈现的,因此只有在用户选择其中一个时才会显示它们。正因为如此,传统的RHF寄存器方法本身并不起作用,因此定制REF是必要的。


When the user selects the first option (Mpesa Buy Goods), and its form fields are rendered, but attempts to submit the form without populating any of the fields, an error is displayed and the first field with an error receives focus, meaning it works as expected. And when they select the second option first (Mpesa Paybill) meaning they didn't select the first option at all, and the respective form fields are rendered, but attempt to submit the form without populating any of the fields, again, an error is displayed for all the form fields not populated and the first field receives focus, meaning that it works as expected too.

当用户选择第一个选项(Mpesa Buy Goods)并呈现其表单域,但试图在不填充任何域的情况下提交该表单时,将显示一个错误,并且第一个出现错误的域获得焦点,这意味着它可以按预期工作。当他们首先选择第二个选项(MPesa PayBill),这意味着他们根本没有选择第一个选项,并且呈现相应的表单域,但尝试在不填充任何域的情况下提交表单,则所有未填充的表单域都会显示错误,并且第一个域获得焦点,这意味着它也可以按预期工作。


The problem is that when the user selects the first option first (Mpesa Buy Goods) and then maybe changes their mind and decides to select the second option instead (Mpesa Paybill), and then attempts to submit the form without populating any of the form fields, the errors are displayed as expected but the first form field with an error does not receive focus. This is inconsistent behavior across UI and not good for UX.

问题是,当用户首先选择第一个选项(MPesa Buy Goods),然后可能改变主意,决定选择第二个选项(MPesa PayBill),然后尝试在不填充任何表单域的情况下提交表单,错误会按预期显示,但出现错误的第一个表单域不会获得焦点。这是整个用户界面不一致的行为,不利于用户体验。


What I've tried:

我试过的是:


I've tried to implement an if block to check if the currently selected option is Mpesa Paybill using selectedPaymentsMode (state variable to track currently rendered mode of payment) and if the field exists in formState.errors when the user attempts to submit the form. If so, trigger validation with trigger with shouldFocus set to true and a similar approach for subsequent fields. This approach ins't working reliably.

我尝试实现一个if块,使用selectedPaymentsMode(跟踪当前呈现的支付模式的状态变量)检查当前选择的选项是否是Mpesa Paybill,以及当用户尝试提交表单时,该字段是否存在于formState.errors中。如果是,则使用shouldFocus设置为true的trigger触发验证,并对后续字段使用类似的方法。这种方法不可靠。


Code:

代码:


Mode of Payment:

付款方式:


          <div>
<h2>
Mode of Payment
<span className="text-red-700 ml-1">*</span>
</h2>
<select
{...register("modeOfPayment", {
onChange: (e) => {
setSelectedPaymentMode(e.target.value);
handleModeOfPaymentChange(e);
},
})}
className={`h-10 w-1/3 text-sm ${
errors?.modeOfPayment?.message
? "border-red-700 focus:border-none focus:ring focus:ring-red-700 focus:ring-2"
: "focus:border-none focus:ring focus:ring-emerald-700 focus:ring-2"
}`}
>
<option value="" disabled={true}>
Choose a mode of payment
</option>
<option>Mpesa Buy Goods</option>
<option>Mpesa Paybill</option>
</select>
{errors?.modeOfPayment?.message && (
<p className=" text-red-700 mt-1">
{errors.modeOfPayment.message}
</p>
)}
</div>

handle mode of payment change:

处理支付方式变更:


  const handleModeOfPaymentChange = (
e: React.ChangeEvent<HTMLSelectElement>
) => {
setSelectedPaymentMode(e.target.value);
};

Mpesa Buy Goods (conditionally rendered):

MPesa Buy Goods(有条件交付):


{selectedPaymentMode === "Mpesa Buy Goods" && (
<div>
<h2>
Enter till number
<span className="text-red-700 ml-1">*</span>
</h2>
<input
{...restTillNumber}
ref={(e) => {
tillNoRef(e);
tillNumberRef.current = e;
}}
type="text"
className={`h-8 w-11/12 self-center text-sm ${
errors?.tillNumber?.message
? "border-red-700 focus:border-none focus:ring focus:ring-red-700 focus:ring-2"
: "focus:border-none focus:ring focus:ring-emerald-700 focus:ring-2"
}`}
/>
{errors?.tillNumber?.message && (
<p className=" text-red-700 mt-1">
{errors.tillNumber.message}
</p>
)}
</div>
)}

Mpesa Paybill (conditionally rendered):

Mpesa工资单(有条件提交):


          {selectedPaymentMode === "Mpesa Paybill" && (
<div>
<div>
<h2>
Enter Paybill number
<span className="text-red-700 ml-1">*</span>
</h2>
<input
type="text"
{...restPaybillNumber}
ref={(e) => {
paybillNoRef(e);
paybillNumberRef.current = e;
}}
className={`h-8 w-11/12 self-center text-sm ${
errors?.paybillNumber?.message
? "border-red-700 focus:border-none focus:ring focus:ring-red-700 focus:ring-2"
: "focus:border-none focus:ring focus:ring-emerald-700 focus:ring-2"
}`}
/>
{errors?.paybillNumber?.message && (
<p className="text-red-700 mt-1">
{errors.paybillNumber.message}
</p>
)}
</div>
<div>
<h2>
Account number
<span className="text-red-700 ml-1">*</span>
</h2>
<input
type="text"
{...restAccountNumber}
ref={(e) => {
accountNoRef(e);
accountNumberRef.current = e;
}}
className={`h-8 w-11/12 self-center text-sm ${
errors?.accountNumber?.message
? "border-red-700 focus:border-none focus:ring focus:ring-red-700 focus:ring-2"
: "focus:border-none focus:ring focus:ring-emerald-700 focus:ring-2"
}`}
/>
{errors?.accountNumber?.message && (
<p className="text-red-700 mt-1">
{errors.accountNumber.message}
</p>
)}
</div>
</div>
)}

track currently rendered option:

跟踪当前渲染选项:


  const [selectedPaymentMode, setSelectedPaymentMode] = useState("");

const watchedPaybillNumber = watch("paybillNumber");
const watchedAccountNumber = watch("accountNumber");

useEffect(() => {
if (
selectedPaymentMode === "Mpesa Paybill" &&
// formState.isSubmitSuccessful === false &&
"paybillNumber" in formState.errors
) {
console.log("error in paybillNumber!");
trigger("paybillNumber", { shouldFocus: true });
// setFocus("paybillNumber");
}

}, [watchedPaybillNumber]);

refs for conditionally rendered form fields:

有条件呈现的表单域的引用:


  const tillNumberRef = useRef<HTMLInputElement | null>(null);
const paybillNumberRef = useRef<HTMLInputElement | null>(null);
const accountNumberRef = useRef<HTMLInputElement | null>(null);

const { ref: tillNoRef, ...restTillNumber } = register("tillNumber");
const { ref: paybillNoRef, ...restPaybillNumber } = register("paybillNumber");
const { ref: accountNoRef, ...restAccountNumber } = register("accountNumber");

FormSchema:

表单架构:


const FormSchema = z.object({
tillNumber: z
.string()
.nonempty({ message: "Please provide a value" })
.min(5, { message: "Please enter a valid till number" })
.max(8, { message: "Please enter a valid till number" }),
paybillNumber: z
.string()
.min(1, { message: "Please provide a value" })
.max(8, { message: "Please enter a valid paybill number" }),
accountNumber: z
.string()
.min(1, { message: "Please provide a value" })
.max(20, { message: "Please enter a valid account number" }),
});

更多回答
优秀答案推荐

You can achieve this by calling RHF reset method which according to the docs, resets the entire form state, fields reference, and subscriptions.

您可以通过调用RHF Reset方法来实现这一点,该方法根据文档重置整个表单状态、字段引用和订阅。


Add it to the useEffect function with selectedPaymentMode as a dependency:

将其添加到useEffect函数中,并将其作为依赖项选定的Payments模式:


  useEffect(() => {
if (selectedPaymentMode === "Mpesa Paybill") {
reset({ tillNumber: "" }); // reset form
clearErrors("tillNumber");
}
if (selectedPaymentMode === "Mpesa Buy Goods") {
reset({ paybillNumber: "", accountNumber: "" });
clearErrors(["paybillNumber", "accountNumber"]);
}
}, [selectedPaymentMode]);

更多回答

35 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com