【问题标题】:Reactjs: Complex Nested forms and managing StateReactjs:复杂的嵌套表单和管理状态
【发布时间】:2022-07-28 02:59:18
【问题描述】:

我正在使用 ReactJS 和 MUI 组件来创建基于以下 JSON 的动态表单。

{
"form": {
    "groups": [{
        "group_id": "acfcbaa6-3bb9-45c0-ad0a-cb5237359bbb",
        "group_name": "Group 1",
        "elements": [{
                "element_id": "dfc81836-3bb9-45c0-ad0a-cb5237359ba9",
                "element_name": "report_type_id",
                "element_type": "S",
                "label": "Select a product",
                "required": false,
                "display": true,
                "options": [{
                        "option_id": "0479f2a1-fd8f-487e-8d46-1f3a5572a6a1",
                        "option_name": "Product 1",
                        "elements": []
                    },
                    {
                        "option_id": "0479f5f1-fd8f-487e-8d46-1f3a5572a6a8",
                        "option_name": "Product 2",
                        "elements": []
                    },
                    {
                        "option_id": "0479sda1-fd8f-487e-8d46-1f3a5572a6a0",
                        "option_name": "Product 3",
                        "elements": []
                    },
                    {
                        "option_id": "0479f2a1-fd8f-487e-8d46-1f3a5572a6bb",
                        "option_name": "Product 4",
                        "elements": []
                    }
                ]
            },
            {
                "element_id": "05bea614-6dc6-4c89-99db-fa6fa791728b",
                "element_name": "product_price",
                "element_type": "T",
                "label": "Product Price",
                "required": false,
                "display": true,
                "options": []
            },
            {
                "element_id": "05bea614-6dc6-4c89-99db-fa6fa791728b",
                "element_name": "product_category",
                "element_type": "S",
                "label": "Product Category",
                "required": false,
                "display": true,
                "options": [{
                        "option_id": "b479f2a1-fd8f-487e-8d46-1cca5572a6a1",
                        "option_name": "Product Cat 1",
                        "elements": []
                    },
                    {
                        "option_id": "b479f5f1-fd8f-487e-6f46-1f3a5572a6a8",
                        "option_name": "Product Cat 2",
                        "elements": []
                    },
                    {
                        "option_id": "0549sda1-fd8f-487e-4d46-1f3a5572a6a0",
                        "option_name": "Product Cat 3",
                        "elements": []
                    },
                    {
                        "option_id": "a479fcb1-fd8f-487e-8d46-1f3a5572a6bb",
                        "option_name": "Product Cat 4",
                        "elements": []
                    }
                ]
            },
            {
                "element_id": "05bea614-6dc6-4c89-99db-fa6fa791728b",
                "element_name": "payment_method",
                "element_type": "S",
                "label": "Payment Method",
                "required": false,
                "display": true,
                "options": [{
                        "option_id": "b479f2a1-fd8f-487e-8d46-1cca5572a6a1",
                        "option_name": "Cash",
                        "elements": []
                    },
                    {
                        "option_id": "0549sda1-fd8f-487e-4d46-1f3a5572a6a0",
                        "option_name": "Credit Card",
                        "elements": [{
                                "element_id": "4745aca7-beb6-4892-9f68-05b1609e56d9",
                                "element_name": "credit_card",
                                "element_type": "CC",
                                "label": "Credit Card"
                            },
                            {
                                "element_id": "42159553-f2c3-4bab-978c-6ac76e65989b",
                                "element_name": "cc_exp_month",
                                "element_type": "T",
                                "label": "Expiration Month"
                            },
                            {
                                "element_id": "9b693880-210b-4946-bf9c-da99a44b3fb6",
                                "element_name": "cc_exp_year",
                                "element_type": "T",
                                "label": "Expiration Year"
                            },
                            {
                                "element_id": "14c7b16d-5595-42fb-ba75-6517ffbd0cdc",
                                "element_name": "cc_code",
                                "element_type": "T",
                                "label": "Code"
                            },
                            {
                                "element_id": "56f6ebf7-4dc0-4609-a2fc-dc7d19859e69",
                                "element_name": "billing_first_name",
                                "element_type": "T",
                                "label": "Billing First Name"
                            },
                            {
                                "element_id": "1f084d95-be3d-4bba-814b-83a51f27e0df",
                                "element_name": "billing_last_name",
                                "element_type": "T",
                                "label": "Billing Last Name"
                            }
                        ]
                    }
                ]
            }
        ]
    }]
}}

如您所见,这个 json 具有嵌套结构,并且嵌套的深度没有限制。所以我们有一个复杂的嵌套形式。问题是——我们如何维护一个表单文档,当表单元素的值发生变化时它会更新。我使用 useState 钩子并在顶层维护一个状态对象,并使用回调将嵌套值传递回父表单(a形成了深度回调链,但完成了工作)。但是,每次更改单个值时,都会渲染整个表单,这不是很理想。任何人都可以提出更好的解决方案吗?专业的 React 开发人员将如何处理这样的用例?

【问题讨论】:

  • 我假设你有一个受控的输入组件,并且表单有很多输入组件,而你只有一个状态对象来承载整个表单的状态,防止不必要计算的理想方法是用 React.memo 包装你的输入组件。
  • “每次单个值更改时,都会呈现整个表单” - 这就是 React 的工作方式。当父组件渲染时,它会渲染其 整个 子 ReactTree。使用 React.memp HOC 将子 ReactTree 中的组件包装起来,以帮助指示/提示 React 可能不需要组件重新渲染 到 DOM,因为传递了 prop 值没有改变。您只分享了您的数据,但如果您分享如何呈现表单和字段,我们可能会提出更好的建议。

标签: reactjs


【解决方案1】:

我认为最好的解决方案是将当前表单模板重新映射为现有视图引擎的格式。例如,您可以尝试jsonformsreact-declaravie

演示应用程序可用on this codesandbox

import { useState } from "react";

import { One, FieldType } from "react-declarative";

const template = {
  form: {
    groups: [
      {
        group_id: "acfcbaa6-3bb9-45c0-ad0a-cb5237359bbb",
        group_name: "Group 1",
        elements: [
          {
            element_id: "dfc81836-3bb9-45c0-ad0a-cb5237359ba9",
            element_name: "report_type_id",
            element_type: "S",
            label: "Select a product",
            required: false,
            display: true,
            options: [
              {
                option_id: "0479f2a1-fd8f-487e-8d46-1f3a5572a6a1",
                option_name: "Product 1",
                elements: []
              },
              {
                option_id: "0479f5f1-fd8f-487e-8d46-1f3a5572a6a8",
                option_name: "Product 2",
                elements: []
              },
              {
                option_id: "0479sda1-fd8f-487e-8d46-1f3a5572a6a0",
                option_name: "Product 3",
                elements: []
              },
              {
                option_id: "0479f2a1-fd8f-487e-8d46-1f3a5572a6bb",
                option_name: "Product 4",
                elements: []
              }
            ]
          },
          {
            element_id: "05bea614-6dc6-4c89-99db-fa6fa791728b",
            element_name: "product_price",
            element_type: "T",
            label: "Product Price",
            required: false,
            display: true,
            options: []
          },
          {
            element_id: "05bea614-6dc6-4c89-99db-fa6fa791728b",
            element_name: "product_category",
            element_type: "S",
            label: "Product Category",
            required: false,
            display: true,
            options: [
              {
                option_id: "b479f2a1-fd8f-487e-8d46-1cca5572a6a1",
                option_name: "Product Cat 1",
                elements: []
              },
              {
                option_id: "b479f5f1-fd8f-487e-6f46-1f3a5572a6a8",
                option_name: "Product Cat 2",
                elements: []
              },
              {
                option_id: "0549sda1-fd8f-487e-4d46-1f3a5572a6a0",
                option_name: "Product Cat 3",
                elements: []
              },
              {
                option_id: "a479fcb1-fd8f-487e-8d46-1f3a5572a6bb",
                option_name: "Product Cat 4",
                elements: []
              }
            ]
          },
          {
            element_id: "05bea614-6dc6-4c89-99db-fa6fa791728b",
            element_name: "payment_method",
            element_type: "S",
            label: "Payment Method",
            required: false,
            display: true,
            options: [
              {
                option_id: "b479f2a1-fd8f-487e-8d46-1cca5572a6a1",
                option_name: "Cash",
                elements: []
              },
              {
                option_id: "0549sda1-fd8f-487e-4d46-1f3a5572a6a0",
                option_name: "Credit Card",
                elements: [
                  {
                    element_id: "4745aca7-beb6-4892-9f68-05b1609e56d9",
                    element_name: "credit_card",
                    element_type: "CC",
                    label: "Credit Card"
                  },
                  {
                    element_id: "42159553-f2c3-4bab-978c-6ac76e65989b",
                    element_name: "cc_exp_month",
                    element_type: "T",
                    label: "Expiration Month"
                  },
                  {
                    element_id: "9b693880-210b-4946-bf9c-da99a44b3fb6",
                    element_name: "cc_exp_year",
                    element_type: "T",
                    label: "Expiration Year"
                  },
                  {
                    element_id: "14c7b16d-5595-42fb-ba75-6517ffbd0cdc",
                    element_name: "cc_code",
                    element_type: "T",
                    label: "Code"
                  },
                  {
                    element_id: "56f6ebf7-4dc0-4609-a2fc-dc7d19859e69",
                    element_name: "billing_first_name",
                    element_type: "T",
                    label: "Billing First Name"
                  },
                  {
                    element_id: "1f084d95-be3d-4bba-814b-83a51f27e0df",
                    element_name: "billing_last_name",
                    element_type: "T",
                    label: "Billing Last Name"
                  }
                ]
              }
            ]
          }
        ]
      }
    ]
  }
};

const fields = template.form.groups
  .map(({ group_name, elements }) => [
    {
      type: FieldType.Line,
      title: group_name
    },
    ...elements.map(({
      element_name,
      element_type,
      options,
      label
    }) => ({
      type: element_type === "S"
        ? FieldType.Combo
        : FieldType.Text,
      name: element_name,
      itemList: options.map(({
        option_name
      }) => option_name),
      title: label
    }))
  ])
  .flat();

export const App = () => {
  const [data, setData] = useState(null);

  const handleChange = (data) => setData(data);

  return (
    <>
      <One fields={fields} onChange={handleChange} />
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </>
  );
};

export default App;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多