【问题标题】:Handling checkboxes with nested array of objects处理带有嵌套对象数组的复选框
【发布时间】:2020-08-18 03:46:26
【问题描述】:

我有一个呈现多个复选框的组件。每个复选框都通过在渲染时分配的 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: []
    }

我先映射一个类别名称,然后再将 checkboxes 映射到相应的值和名称,例如:

Vegetables
checkbox 1
checkbox 2

Fruits
checkbox 3
checkbox 4

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

非常感谢任何解决此问题的帮助。

【问题讨论】:

    标签: javascript reactjs checkbox


    【解决方案1】:

    我分叉了您的 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>
      );
    };
    

    【讨论】:

    • 非常感谢这位朋友的帮助。我确实解决了这些功能和映射问题。正要更新沙箱,同时,你也帮忙了:)你能帮我理解一下在 setState() 中使用传播运算符与回调的用例吗?
    • 这很好,你的总结很好,但如果链接随着时间的推移发生变化或关闭,对未来的访问者没有多大帮助。您能否在答案本身中包含足够的代码以独立作为资源? OP 还隐藏了链接后面的大部分相关代码,这与 SO 的独立资源管理导向方法并不相符。
    • 我已更新我的答案以包含内联代码。 @bubble-cord 如果您的新状态值是根据当前/先前状态值计算的,您将需要使用回调方法来确保始终从最新状态计算新状态。如果您正在设置状态并且不需要计算旧状态值,那么您可以简单地传入新状态值。如果您有更多问题,则需要创建新问题或搜索现有答案。
    • @Chris 嗨。你能帮我解决另一个与此相关的问题吗?
    • @bubble-cord 在 cmets 中进行扩展讨论和其他问题通常在 Stack Overflow 社区中是不鼓励的。如果您想进一步讨论,我的个人资料中有联系信息。
    【解决方案2】:

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

    以下是无效的 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 的使用。您还可以使用较新的 short syntax (&lt;&gt;...&lt;/&gt;),它看起来更干净 (but lacks some tool support)。您还可以通过使用箭头函数的隐式返回值来进一步清理代码。

    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>
    );
    

    【讨论】:

      【解决方案3】:

      在@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"));
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-12-20
        • 1970-01-01
        • 2021-08-11
        • 1970-01-01
        • 2021-07-08
        相关资源
        最近更新 更多