如何在react-hook-form中将数值注册为嵌套属性?

问题描述 投票:0回答:1

我有一个名为 Totals 的属性,它应该包含多个嵌套对象作为属性。

这是我想要的表单结构:

{
  totals: {
      0: {
        value: 0,
      },
    },
 };

然而,react-hook-form 文档指出,将数字设置为嵌套属性会在指示的索引处创建值,因此此代码

setValue(`totals.2`, {
  value: 0,
});

给了我这个不想要的结果:

{
  totals: [, , {
        value: 0,
      }]
 };

关于如何设置数字嵌套属性有什么想法吗?

reactjs react-hook-form
1个回答
1
投票

这不在文档中,但有一种方法可以绕过此限制。

第一次

register()
包含子字段的字段时,库将检查该子字段是数字还是文本。
如果它是一个数字,它将创建一个数组,否则它将创建一个对象。
对于每个后续
register()
,它只会在该对象或数组上添加子字段,而不检查子字段是否是数字。


这是我们如何使用它的演示:

// first register a dummy text field
// this will cause field `foo` to be an object with one sub-field `bar`
register('foo.bar');

// then register our numeric sub-fields
register('foo.1');
register('foo.2.value');

// then get rid of the dummy sub-field `bar`
unregister('foo.bar');

// now we can set the value of those numeric fields
// and you don't have to worry about it turning into an array
setValue('foo.1', 'lorem');
setValue('foo.2.value', 'ipsum');

// here's what we get at the end
console.log(getValues());
const result = {
    "foo": {
        "1": "lorem",
        "2": {
            "value": "ipsum"
        }
    }
}

这样我们就可以构造这个辅助函数了:

const getRegisterOnObject = (register, unregister) => (path, options) =>
{
    const tempPath = path.replace(/(\d+)/g, '__$1');
    if (tempPath !== path) // if there are numbers in the path we need a dummy field
    {
        register(tempPath);
        const res = register(path, options);
        unregister(tempPath);
        return res;
    }
    else // if there are no numbers then we should not create a dummy field
    {
        return register(path, options);
    }
};

每次您想要注册一个字段时,它都会临时创建一个虚拟字段,并在不需要时将其删除。
虚拟字段将如下所示:

foo.__2
foo.__2.value
等...

然后我们可以在我们的组件中使用它来扩展基本的

register()
功能:

// get the modified `register()` function
const registerOnObject = getRegisterOnObject(register, unregister);

// proceed to register fields like you usually would
registerOnObject('foo.2');

// if you are only registering in one place then it is simpler to write it in one line
getRegisterOnObject(register, unregister)('foo.2');

在之前版本的答案中我建议使用钩子来创建虚拟字段,但我相信上面的更实用。

旧答案:

您需要将其放在对

useForm()

的呼叫下方
// first we register() our dummy field in useMemo()
// because useMemo() is called right away
useMemo(() => {register('foo.bar')}, []);
// then we unregister() it inside useEffect()
// because useEffect() is called after our component is rendered
useEffect(() => {unregister('foo.bar')}, []);

这样,您将在第一次渲染期间获得

foo.bar
,并且库会将数字字段放在
foo
对象下。

如果您有多个这样的字段,则必须初始化所有这些字段,如下所示:

useMemo(() => {register('fooA.bar');register('fooB.bar')}, []);
useEffect(() => {unregister('fooA.bar');unregister('fooB.bar')}, []);
© www.soinside.com 2019 - 2024. All rights reserved.