根据 objects 的数组形成组件

Forming the components based on the array of objects

对于应用程序的设置页面,我实现了一个启用(绿色)或禁用(红色)状态的滑块。而parent的设置是根据其children.

的值计算出来的
//Getting the switches configuration inside componnetDidMount something like this

var obj = [
  {
    parent_header_name: "parent1",
    children_info: [
      {
        child_switch_name: "child1",
        isEnabled: true
      },
      {
        child_switch_name: "child2",
        isEnabled: false
      }
    ]
  },
  {
    parent_header_name: "parent2",
    children_info: [
      {
        child_switch_name: "child3",
        isEnabled: true
      }
    ]
  },
  {
    parent_header_name: "parent3",
    children_info: [
      {
        child_switch_name: "child4",
        isEnabled: false
      }
    ]
  }
];

现在根据这个值,我需要形成一个 parent 和 children 的分组,如下所示:

Label(the value should be parent_header_name) : Parent Switch Component
Label for children(children_switch_name) : Child Switch Component

另外,在切换单个 children 开关时,我需要像这样获取该开关的信息:

例如,将 parent1 的 child1 改为残疾

[
  {
    parent_header_name: "parent1",
    children_info: [
      {
        child_switch_name: "child1",
        isEnabled: false
      }
    ]
  }
];

万一 parent1 启用,我需要获取它的所有子项值

[
  {
    parent_header_name: "parent1",
    children_info: [
      {
        child_switch_name: "child1",
        isEnabled: true
      },
      {
        child_switch_name: "child2",
        isEnabled: true
      }
    ]
  }
]

并且当 parents 开关被切换时(当 parent 启用时,children 将被启用,当禁用时 children 将被禁用;),我需要获取 parent

的全部信息

此外,我需要避免切换到 "partial" 状态,parent 应该只启用或禁用。 "Partial"只是代表性的

为此,我将 react-multi-toggle 用于此切换开关。

我试过这样的事情:https://codesandbox.io/s/parent-child-switches-gxfx6

我会这样做:

  • 你有一个顶级 Settings class 获取设置对象,将其保存为状态并映射数组,创建 ParentSwitch 个实例,传递完整的父设置,包括其子项和更新 Settings 状态的函数(例如,使用两个整数参数 - 父索引和子索引)作为 ParentSwitch

    [= 的道具43=]
  • ParentSwitch 然后根据其 props 创建 ChildSwitches 并将通过其 props 获得的 updateSettings 函数传递给 ChildSwitch

  • ChildSwitch切换时,调用Settings中的函数,切换Settings的状态,传给ParentSwitch以及显示的ChildSwitch

这样可以理解吗?有什么不清楚的地方欢迎提问

编辑: 这是工作代码笔。你必须自己布局,我已经花了足够多的时间在这上面。 https://codesandbox.io/s/parent-child-switches-jq0bp?fontsize=14 我所有的代码都在 Switches.js

您可以重构 Setting component 以允许它将您的开关 name and value according 呈现为从 API 接收到的数据。我建议您为每个 switches group 添加一个 id,这将简化您的工作。 Here is forked working sandbox。可以优化代码以适合您的用例。关键更改在 Setting Component.

中完成

设置组件完整代码

import React, { Component, Fragment } from "react";
import isEqual from "lodash.isequal";

import ChildSwitch from "./ChildSwitch";
import ParentSwitch from "./ParentSwitch";
import { PARTIAL } from "./constant";

export default class Setting extends Component {
  state = {
    parent: {
      value:
        this.props.children.length > 1
          ? PARTIAL
          : this.props.children[0].isEnabled
    },
    children: this.props.children
  };

  componentDidMount() {
    this.setParentSwitchValue();
  }

  shouldComponentUpdate(nextProps, nextState) {
    return !isEqual(this.state, nextState);
  }

  setChildSwitchValue = (id, isEnabled) => {
    let clickedChild;
    this.setState(
      prevState => ({
        ...prevState,
        children: prevState.children.map(child => {
          if (child.id === id) {
            clickedChild = { ...child, isEnabled: isEnabled };
            return clickedChild;
          } else {
            return child;
          }
        })
      }),
      () => this.setParentSwitchValue(clickedChild)
    );
  };

  setParentSwitchValue = clickedChild => {
    const { children } = this.state;
    let parentVal = PARTIAL;

    if (children.every(({ isEnabled }) => isEnabled === true)) {
      parentVal = true;
    }
    if (children.every(({ isEnabled }) => isEnabled === false)) {
      parentVal = false;
    }

    this.setState(
      prevState => ({
        ...prevState,
        parent: {
          value: parentVal
        }
      }),
      () => {
        this.handleChange();
        if (clickedChild) {
          const changed = {
            parent: {
              name: this.props.name,
              value: parentVal
            },
            child: clickedChild
          };
          console.log("This is the changed child", changed);
        }
      }
    );
  };

  setChildrenValue = value => {
    this.setState(
      prevState => ({
        ...prevState,
        parent: {
          value
        },
        children: prevState.children.map(child => ({
          ...child,
          isEnabled: value
        }))
      }),
      this.handleChange
    );
  };

  handleChange = () => {
    const { id, onChange } = this.props;
    onChange(id, this.state);
  };

  handleParentClick = parentVal => {
    if (parentVal !== PARTIAL) {
      this.setChildrenValue(parentVal);
    }
  };

  render() {
    const { parent, children } = this.state;
    const { name } = this.props;
    return (
      <div className="boxed">
        <span>{name}</span>
        <ParentSwitch
          childrenCount={children.length}
          parentSwitch={parent.value}
          onSelect={this.handleParentClick}
        />
        {children.map(({ id, name, isEnabled }) => (
          <Fragment key={id}>
            <span>{name}</span>
            <ChildSwitch
              switchName={id}
              selected={isEnabled}
              onSelect={this.setChildSwitchValue}
            />
          </Fragment>
        ))}
      </div>
    );
  }
}