formik 嵌套动态对象
formik nested dynamic object
能否请您针对以下问题提出解决方案:
例如我们有一个包含许多交付选项的结帐表格,每个选项都需要不同的详细信息,例如:
- 店内 - 只需要
storeId
- 送货办公室 - 需要
cityId
和 officeId
- 快递 - 需要
address
个字段(街道、建筑物)
状态示例:
...,
delivery: {
type: 'inStore',
details: { storeId: 1 }
}
或
...,
delivery: {
type: 'courier',
details: {
address: {
street: '12 Ave',
...,
}
}
}
一旦只能检查上述选项之一,我就尝试使用单选按钮组件来定义 delivery type
此外,还可以根据 delivery type
呈现不同的组件(例如通过 switch-case)并填充不同的 delivery.___
字段。
但是如果我们填写 inStore
选项的表格然后切换到例如courier
选项storeId
属性仍处于状态。那么如何正确处理这个开关(根据所选 delivery type
)
只剩下必需的属性
在你的案例中,React 尝试为 store
和 courier
的第一个 Field
和 courier
案例重新使用相同的输入组件,因此它不必呈现一个全新的节点。您可以通过向每个 Field
组件添加一个独特的 key
道具来轻松解决此问题:
switch (deliveryType) {
case "store":
return (
<Field
key={`${name}.storeId`}
name={`${name}.storeId`}
value={1}
placeholder="storeId"
/>
);
case "courier":
return (
<React.Fragment>
<Field
key={`${name}.address.street`}
name={`${name}.address.street`}
placeholder="street"
/>
<Field
key={`${name}.address.building`}
name={`${name}.address.building`}
placeholder="building"
/>
<Field
key={`${name}.address.flat`}
name={`${name}.address.flat`}
placeholder="flat"
/>
</React.Fragment>
);
case "delivery_office":
return (
<React.Fragment>
<Field
key={`${name}.cityId`}
name={`${name}.cityId`}
placeholder="cityId"
/>
<Field
key={`${name}.officeId`}
name={`${name}.officeId`}
placeholder="officeId"
/>
</React.Fragment>
);
default:
return null;
}
至于现在,我已将子表单状态重置添加到单选按钮 onChange
事件处理程序。因此基于新的单选按钮值(例如deliveryType
)计算出新的初始状态(例如deliveryDetails
)。
正因为如此,新的 deliveryType
和新的 initialState
允许渲染相关的子表单没有任何问题。
请在此处检查解决方案:https://codesandbox.io/s/formik-example-forked-ix0nt?file=/deliveryDetailsForm.js
值得一提的是,需要先更新子表单的初始状态,再更新因变量(如deliveryType
)。因此,一旦 values.delivery.details
对象在 在 之前更新所需的子表单,就不会发生 'controlled-to-uncontrolled' 错误。
P.S。当前的解决方案使用 switch-case
来决定应该呈现哪个 initialState
和哪个子表单,这是一个非常糟糕的设计,所以如果您有任何关于如何让它变得更好的建议,我会很高兴听到他们。
能否请您针对以下问题提出解决方案:
例如我们有一个包含许多交付选项的结帐表格,每个选项都需要不同的详细信息,例如:
- 店内 - 只需要
storeId
- 送货办公室 - 需要
cityId
和officeId
- 快递 - 需要
address
个字段(街道、建筑物)
状态示例:
...,
delivery: {
type: 'inStore',
details: { storeId: 1 }
}
或
...,
delivery: {
type: 'courier',
details: {
address: {
street: '12 Ave',
...,
}
}
}
一旦只能检查上述选项之一,我就尝试使用单选按钮组件来定义 delivery type
此外,还可以根据 delivery type
呈现不同的组件(例如通过 switch-case)并填充不同的 delivery.___
字段。
但是如果我们填写 inStore
选项的表格然后切换到例如courier
选项storeId
属性仍处于状态。那么如何正确处理这个开关(根据所选 delivery type
)
在你的案例中,React 尝试为 store
和 courier
的第一个 Field
和 courier
案例重新使用相同的输入组件,因此它不必呈现一个全新的节点。您可以通过向每个 Field
组件添加一个独特的 key
道具来轻松解决此问题:
switch (deliveryType) {
case "store":
return (
<Field
key={`${name}.storeId`}
name={`${name}.storeId`}
value={1}
placeholder="storeId"
/>
);
case "courier":
return (
<React.Fragment>
<Field
key={`${name}.address.street`}
name={`${name}.address.street`}
placeholder="street"
/>
<Field
key={`${name}.address.building`}
name={`${name}.address.building`}
placeholder="building"
/>
<Field
key={`${name}.address.flat`}
name={`${name}.address.flat`}
placeholder="flat"
/>
</React.Fragment>
);
case "delivery_office":
return (
<React.Fragment>
<Field
key={`${name}.cityId`}
name={`${name}.cityId`}
placeholder="cityId"
/>
<Field
key={`${name}.officeId`}
name={`${name}.officeId`}
placeholder="officeId"
/>
</React.Fragment>
);
default:
return null;
}
至于现在,我已将子表单状态重置添加到单选按钮 onChange
事件处理程序。因此基于新的单选按钮值(例如deliveryType
)计算出新的初始状态(例如deliveryDetails
)。
正因为如此,新的 deliveryType
和新的 initialState
允许渲染相关的子表单没有任何问题。
请在此处检查解决方案:https://codesandbox.io/s/formik-example-forked-ix0nt?file=/deliveryDetailsForm.js
值得一提的是,需要先更新子表单的初始状态,再更新因变量(如deliveryType
)。因此,一旦 values.delivery.details
对象在 在 之前更新所需的子表单,就不会发生 'controlled-to-uncontrolled' 错误。
P.S。当前的解决方案使用 switch-case
来决定应该呈现哪个 initialState
和哪个子表单,这是一个非常糟糕的设计,所以如果您有任何关于如何让它变得更好的建议,我会很高兴听到他们。