开发者问题收集

使用嵌套对象数组处理复选框

2020-08-17
5229

我有一个可渲染多个复选框的组件。每个复选框都通过其 checked 属性进行动态处理,并在渲染时进行分配。我需要打印/渲染选中复选框的值。 示例状态如下所示:

{
        Filters: [
          {
            name: "Vegetables",
            options: [
              {
                value: "tmto",
                name: "Tomato"
              },
              {
                value: "ptato",
                name: "Potato"
              }
            ]
          },
          {
            name: "Fruits",
            options: [
              {
                value: "ornge",
                name: "Orange"
              },
              {
                value: "grps",
                name: "Grapes"
              }
            ]
          }
        ],
      selected: []
    }

我首先映射类别名称,然后再次使用相应的值和名称映射 checkbox ,例如:

Vegetables
checkbox 1
checkbox 2

Fruits
checkbox 3
checkbox 4

我有一个适用于相同 https://codesandbox.io/s/react- functional-component-forked-wb1ew 的工作代码片段,但不确定为什么它无法设置与选中复选框匹配的状态。 我对如何处理这种嵌套复选框状态有点困惑。

任何有助于解决此问题的帮助都将不胜感激。

3个回答

我分叉了您的 CodeSandbox 并应用了修复。您可以在下面找到可行的解决方案。

https://codesandbox.io/s/react- functional-component-forked-gmwv0


有很多需要更改的地方,但我会尽力介绍。

  1. 您在 handleCheckboxChange 中引用了 this.setState ,这在功能组件中不起作用。在功能组件中,您使用 useState 钩子的第二个参数,在本例中等于 setState 。功能组件不会将 this 用于任何内部方法。
  2. 您的渲染函数内部存在无效语法。您需要确保始终返回带有单个顶级包装组件的 JSX 对象。
  3. 其余问题都出现在您的 handleCheckboxChange 中。由于 setState 的异步特性,最好在 setState 内部使用回调,即 setState((prevState) => {}) ,这将确保您没有陈旧状态。您还需要从中返回一个对象,这将是您的新状态。
  4. 我更新了复选框的值(已选中),以使用您在此函数中计算的选定值。
  5. 我做的最后一件事是更新映射以正确返回正确格式的状态。

最终解决方案

const App = (props) => {
  const [state, setState] = useState({
    Filters: [
      {
        name: "Vegetables",
        options: [
          {
            value: "tmto",
            name: "Tomato"
          },
          {
            value: "ptato",
            name: "Potato"
          }
        ]
      },
      {
        name: "Fruits",
        options: [
          {
            value: "ornge",
            name: "Orange"
          },
          {
            value: "grps",
            name: "Grapes"
          }
        ]
      }
    ],
    selected: []
  });

  const handleCheckboxChange = (value) => {
    setState((state) => {
      const updatedEtables = state.selected.find((obj) => obj === value);
      const selected = updatedEtables
        ? state.selected.filter((obj) => obj !== value)
        : [...state.selected, value];

      return {
        selected,
        Filters: [
          ...state.Filters.map((filter) => {
            return {
              ...filter,
              options: filter.options.map((ele) => {
                return {
                  ...ele,
                  checked: selected.includes(ele.value)
                };
              })
            };
          })
        ]
      };
    });
  };

  return (
    <div>
      {state.Filters.map((ele) => {
        return (
          <React.Fragment>
            <h6>{ele.name}</h6>
            {ele.options.map((item) => {
              return (
                <div>
                  <input
                    checked={item.checked || false}
                    onChange={() => handleCheckboxChange(item.value)}
                    type="checkbox"
                  />
                </div>
              );
            })}
          </React.Fragment>
        );
      })}

      <strong>Selected: </strong>
      <br />
      <span>{state.selected.join(",")}</span>
    </div>
  );
};
Chris
2020-08-17

链接的代码会引发错误,因为 {...} 符号必须始终夹在标签之间。

以下是无效的 JSX:

return (
  <h1>Hello World!</h1>
  {data}
);

JSX 必须始终有一个单独的根组件。如果您想要返回多个根级别组件,请将它们包装在 React.Fragment 中。

上述无效示例应写为:

return (
  <React.Fragment>
    <h1>Hello World!</h1>
    {data}
  </React.Fragment>
);

这可确保只有一个根组件可以解决您的错误。


将其应用到您的代码中会将:

return (
  <div>
    {state.Filters.map(ele => {
      return (
        <h6>{ele.name}</h6>
        {ele.options.map(item => {
          return (
            <div>
              <input 
                checked={item.checked}
                onChange={() => handleCheckboxChange(item.value)}
                type="checkbox"
              />
            </div>
          )
        })}
      )
    })}
    <strong>Selected: </strong>
    <br />
    <span>{state.selected.join(",")}</span>
  </div>
);

更改为:

return (
  <div>
    {state.Filters.map(ele => {
      return (
        <React.Fragment>
          <h6>{ele.name}</h6>
          {ele.options.map(item => {
            return (
              <div>
                <input 
                  checked={item.checked}
                  onChange={() => handleCheckboxChange(item.value)}
                  type="checkbox"
                />
              </div>
            )
          })}
        </React.Fragment>
      )
    })}
    <strong>Selected: </strong>
    <br />
    <span>{state.selected.join(",")}</span>
  </div>
);

请注意 React.Fragment 的使用。您还可以使用较新的 简短语法 ( <>...</> ),它看起来更简洁( 但缺少一些工具支持 )。您还可以使用箭头函数的隐式返回值进一步清理代码。

return (
  <div>
    {state.Filters.map(ele => (
      <>
        <h6>{ele.name}</h6>
        {ele.options.map(item => (
          <div>
            <input 
              checked={item.checked}
              onChange={() => handleCheckboxChange(item.value)}
              type="checkbox"
            />
          </div>
        ))}
      </>
    ))}
    <strong>Selected: </strong>
    <br />
    <span>{state.selected.join(",")}</span>
  </div>
);
3limin4t0r
2020-08-17

在@Chris 的帮助和@3limin4t0r 的宝贵意见的帮助下,我按照@ggorlen 的建议,在这里编译了最终的工作代码,以供将来可能遇到相同问题的人使用:

import React, { useState } from "react";
import { render } from "react-dom";

const App = (props) => {
  const [state, setState] = useState({
    Filters: [
      {
        name: "Vegetables",
        options: [
          {
            value: "tmto",
            name: "Tomato"
          },
          {
            value: "ptato",
            name: "Potato"
          }
        ]
      },
      {
        name: "Fruits",
        options: [
          {
            value: "ornge",
            name: "Orange"
          },
          {
            value: "grps",
            name: "Grapes"
          }
        ]
      }
    ],
    selected: []
  });

  const handleCheckboxChange = (value) => {
    setState((state) => {
      const updatedEtables = state.selected.find(
        (obj) => obj === value
      );
      const selected = updatedEtables
        ? state.selected.filter(
            (obj) => obj !== value
          )
        : [...state.selected, value];

      return {
        selected,
        Filters: [
          ...state.Filters.map((filter) => {
            return {
              ...filter,
              options: filter.options.map((ele) => {
                return {
                  ...ele,
                  checked: selected.includes(ele.value)
                };
              })
            };
          })
        ]
      };
    });
  };

  return (
    <div>
      {state.Filters.map((ele) => {
        return (
          <React.Fragment>
            <h6>{ele.name}</h6>
            {ele.options.map((item) => {
              return (
                <div>
                  <input
                    checked={item.checked || false}
                    onChange={() => handleCheckboxChange(item.value)}
                    type="checkbox"
                  />
                </div>
              );
            })}
          </React.Fragment>
        );
      })}

      <strong>Selected: </strong>
      <br />
      <span>{state.selected.join(",")}</span>
    </div>
  );
};

render(<App />, document.getElementById("root"));
program_bumble_bee
2020-08-17