首页 > 解决方案 > 使用 HTML 元素(输入、收音机等)递归呈现 JSON 树

问题描述

我对 React 和使用 JSON 结构非常陌生。我正在尝试构建 JSON 树结构的递归渲染,该结构从树中动态渲染单个 HTML 元素(例如单选按钮、下拉菜单等)。我见过其他实现,但它们没有嵌套的 HTML 元素,这些元素不同于 li、ul 等。它们通常在树的下方也没有不同的命名约定(例如属性、选项)。

树看起来像这样:

    {
        "id": "1",
        "name": "Animals",
        "color": "#e37939",
        "shape": "bounding_box",
        "attributes": [
            {
                "id": "1.1",
                "name": "Type",
                "type": "radio",
                "required": false,
                "options": [
                    {
                        "id": "1.1.1",
                        "optionName": "Cats",
                        "optionValue": "cats",
                        "options": [.... and so on

    };

我最终要实现的是获得一种格式,其中单击“动物按钮”,然后呈现嵌套的单选按钮,如果选择“猫”选项值,它将呈现下一个下拉菜单。我已经设置了一组初始方法,但我不太清楚如何在单击选项时动态呈现下一组嵌套选项。我在这里创建了一个 React fiddle:https ://codesandbox.io/s/vigilant-grothendieck-jknym

最大的挑战是将嵌套递归选项嵌入选项组中。我还没有弄清楚如何做到这一点。

标签: jsonreactjs

解决方案


我已经为您想要实现的目标创建了一个数据结构,尽管我已经对其进行了一些调整,因为它的某些部分是多余的,但是您仍然可以保留数据结构并在它们之间进行转换。它可以递归地进行到您想要的深度.

const prodList = [
 {
    id: "1",
    name: "Animals",
    options: [
      {
        id: "1.1",
        name: "Type",
        inputType: "radio",
        options: [
          {
            id: "1.1.1",
            name: "Cats",
            value: "Cats",
            inputType: "select",
            options: [
              { id: "1.1.1.1", name: "Siamese Grey", value: "Siamese Grey" },
              { id: "1.1.1.2", name: "Siamese Black", value: "Siamese Black" },
              { id: "1.1.1.3", name: "Siamese Cute", value: "Siamese Cute" },
              { id: "1.1.1.4", name: "House Cat", value: "House Cat" },
              { id: "1.1.1.5", name: "House Cat", value: "House Cat" }
            ]
          },
          { id: "1.1.2", name: "Dogs", value: "Dogs" },
          { id: "1.1.3", name: "Cows", value: "Cows" }
        ]
      }
    ]
  }
];

上面是具有“inputType”属性的数据结构,该属性有助于确定要显示的组件。我们将有一个基本组件、一个无线电组件和一个可以在它们内部相互渲染的每种类型的选择组件。

export default class ProductsPage extends Component {
  render() {
    let prodItems = prodList.map(p => {
      return <MainContentManager data={p} key={p.id} />;
    });
    return <div>{prodItems}</div>;
  }
}

class MainContentManager extends Component {
  render() {
    let renderObj = null;
    renderObj = basicMethod(renderObj, this.props.data);
    return (
      <div>
        <h6> {this.props.data.name}</h6>
        {renderObj}
      </div>
    );
  }
}

class RadioButtonManager extends Component {
  constructor(props) {
    super(props);
    this.state = {
      activeOptionIndex: 0
    };
    this.handleInputClick = this.handleInputClick.bind(this);
  }
  handleInputClick(index) {
    this.setState({
      activeOptionIndex: index
    });
  }
  render() {
    let renderObj = null;
    let renderDat = null;
    renderDat = this.props.data.options.map((op, index) => {
      return (
        <label key={op.id}>
          <input
            type="radio"
            onChange={e => {
              this.handleInputClick(index);
            }}
            checked={index == this.state.activeOptionIndex ? true : false}
          />
          {op.name}
        </label>
      );
    });

    renderObj = basicMethod(renderObj, {
      options: [this.props.data.options[this.state.activeOptionIndex]]
    });
    return (
      <div>
        <h6> {this.props.data.name}</h6>
        {renderDat}
        {renderObj}
      </div>
    );
  }
}

class SelectManager extends Component {
  constructor(props) {
    super(props);
    this.state = { value: "", activeOptionIndex: 0 };
    this.handleInputClick = this.handleInputClick.bind(this);
  }

  handleInputClick(value) {
    let activeOptionIndex = this.state.activeOptionIndex;
    if (this.props.data.options) {
      for (let i = 0; i < this.props.data.options.length; i++) {
        if (this.props.data.options[i].value == value) {
          activeOptionIndex = i;
        }
      }
    }
    this.setState({
      value: value,
      activeOptionIndex: activeOptionIndex
    });
  }
  render() {
    let renderObj = null;
    let selectOptions = this.props.data.options.map((op, index) => {
      return (
        <option key={op.value} value={op.value}>
          {op.name}
        </option>
      );
    });
    renderObj = basicMethod(renderObj, {
      options: [this.props.data.options[this.state.activeOptionIndex]]
    });
    return (
      <div>
        <select
          onChange={e => {
            this.handleInputClick(e.target.value);
          }}
        >
          {selectOptions}
        </select>
        {renderObj}
      </div>
    );
  }
}

function basicMethod(renderObj, data) {
  if (data && data.options) {
    renderObj = data.options.map(op => {
      !op && console.log(data);
      let comp = null;
      if (op.inputType == "radio") {
        comp = <RadioButtonManager data={op} key={op.id} />;
      } else if (op.inputType == "select") {
        comp = <SelectManager data={op} key={op.id} />;
      } else {
        comp = <MainContentManager data={op} key={op.id} />;
      }
      return comp;
    });
  }

  return renderObj;
}

如果不清楚或者你想要它有点不同,请问任何事情。


推荐阅读