为什么在 react-hook-form 中对动态表单使用 useFieldArray() 时不能关注当前字段?

Why can't I focus on the current field when using useFieldArray() for dynamic form in react-hook-form?

使用 react-hook-form 7.14.2。我有一个动态表单,并且正在使用内置验证。我觉得 react-hook-form 不能非常优雅地处理动态表单,我可能应该停止对此进行搅动,并退回到普通的 HTML 表单。

我只想在验证发生后专注于我正在输入的字段。这些字段是必需的,但是在显示验证消息并且我开始输入之后,会发生重新渲染并且焦点会丢失在当前字段上......它会跳回到表单中的最后一个字段。这是一个笨拙、糟糕的用户体验,我似乎无法找到技巧和技术的神奇组合来让它像普通的 react-hook-form 表单一样工作!

表格:

import React, { useEffect } from "react";
import { useForm, useFieldArray } from "react-hook-form";
import { Icon } from "@iconify/react";

import socials from "../../enum/social";

const SocialLinksEdit = props => {
    const {
        socialLinks,
        onSave,
        onCancel
    } = props;

    const { control, register, setValue, setFocus, handleSubmit, formState: { errors } } = useForm();

    const { fields, append } = useFieldArray({
        control,
        name: "socialItems"
    });

    useEffect(() => {
        Object.entries(socials).find(([, s]) => append(s));
    }, []);

    const getErrorMessage = i => {
        if (!errors || Object.keys(errors).length === 0) {
            return;
        }
        if (errors.socialItems && errors.socialItems[i]) {
            return errors.socialItems[i].url.message;
        }
        return null;
    };

    const SocialItems = () => {
        const onFocus = field => {
            // setValue(e.target.value);
            setFocus(field);
        };
        const onFocusOut = e => {
            setValue(e.target.value);
        };
        return (
            <>
                {
                    fields.map((field, i) => {
                        let socialData = socialLinks.find(sl => sl.social_service === field.name);
                        console.log(errors);
                        return (
                            <div key={i} className="modal-input-field modal-input-mb">
                                <div className="modal-label-container">
                                    <label className="modal-label">
                                        <span className="modal-label-icon"><Icon icon={field.icon} /></span>
                                        {field.label}
                                    </label>
                                </div>
                                <input
                                    type="text"
                                    // autoFocus={i === 0}
                                    ref={control}
                                    autoFocus
                                    // onFocus={() => onFocus(`socialItems.${i}.url`)}
                                    // onFocusOut={onFocusOut}
                                    {...register(`socialItems.${i}.url`, { required: "URL is required!" })}
                                    defaultValue={socialData?.url}
                                />
                                {errors && <span className="validation">{getErrorMessage(i)}</span>}
                            </div>
                        );
                    })
                }
            </>
        );
    };

    return (
        <div className="modal-container">
            <div>
                <form onSubmit={handleSubmit(onSave)}>
                    <div className="modal-title-container">
                        <div className="modal-title-icon"><Icon icon="bi:megaphone" /></div>
                        <div className="modal-title">Social Links</div>
                    </div>
                    <SocialItems />
                    <div className="modal-buttons">
                        <button type="submit" className="form-button-primary">Save</button>
                        <button type="button" className="form-button-cancel" onClick={e => onCancel(e)}>Cancel</button>
                    </div>
                </form>
            </div>
        </div>
    );
};

export default SocialLinksEdit;

一些 'splainin' 事情要做:

这是一个关于其行为方式的快速视频。如果我能让字段保持焦点而不是像这样跳到底部字段,我会很开心。

https://www.tiktok.com/@zambizzi/video/7093151720624147755?is_from_webapp=1&sender_device=pc&web_id=7039430208454280710

原来,只是将字段直接嵌套在内部,而不是在单独的组件中,以防止 re-render:

return (
    <div className="modal-container">
        <div>
            <form onSubmit={handleSubmit(onSave)}>
                <div className="modal-title-container">
                    <div className="modal-title-icon"><Icon icon="bi:megaphone" /></div>
                    <div className="modal-title">Social Links</div>
                </div>
                {
                    fields.map((field, i) => {
                        let socialData = socialLinks.find(sl => sl.social_service === field.name);
                        console.log(errors);
                        return (
                            <div key={i} className="modal-input-field modal-input-mb">
                                <div className="modal-label-container">
                                    <label className="modal-label">
                                        <span className="modal-label-icon"><Icon icon={field.icon} /></span>
                                        {field.label}
                                    </label>
                                </div>
                                <div>
                                    <input
                                        type="text"
                                        autoFocus
                                        {...register(`socialItems.${i}.url`, { required: "URL is required!" })}
                                        defaultValue={socialData?.url}
                                    />
                                    {errors && <span className="validation">{getErrorMessage(i)}</span>}
                                </div>
                            </div>
                        );
                    })
                }
                <div className="modal-buttons">
                    <button type="submit" className="form-button-primary">Save</button>
                </div>
            </form>
        </div>
    </div>
);