3

I have a data coming from API as follows -

{
  "expression": [
    "a",
    "b"
  ],
  "groups": [
    {
      "expression": [

      ],
      "groups": [
        {
          "expression": [
            "c",
            "d"
          ],
          groups: null
        }
      ]
    },
    {
      "expression": [
        "p",

      ],
      groups: null
    }
  ]
}

I have to render Expressions and Groups (that includes expressions and multiple groups as well). The building block is an expression. We need to display nested expressions as groups.

I tried with recursive function call, but that is not working with much efficiency for an object with large depth.

Here is something similar to my trial -

let conditionStack = [];
  function parseInnerConditions(conditionObj) {
    if (conditionObj.expression) {
      conditionObj.expression.map((each, index) => {
        conditionStack = [
          ...conditionStack,
          <NewExpression key={index} conditionExpression={each} />, //renders an expression
        ];
      });
    }
    if (conditionObj.groups && conditionObj.groups.length) {
      return conditionObj.groups.map((child, index) => {
        conditionStack = [
          ...conditionStack,
          <ConditionGroup key={index}> // renders a block or group of expressions
            {parseInnerConditions(child)}
          </ConditionGroup>,
        ];
      });
    }
    return conditionStack;
  }

  return (
    <div>
    parseInnerConditions(conditionObject)
    </div>
  )

Thanks in advance.

2 Answers2

2

ReactJS is a Component-Based library for generating UIs. So thinking in terms of Components your data is a Group which in turn will contain an expression and other groups like itself. Breaking this down we can form three components:

  1. Expression For simply rendering the expression in a group.
  2. Group For rendering a group.
  3. Groups For extracting and then rendering Group component for further groups in your data.

Here is a sample code:

Index.js This is the file getting data from API.

import React from "react";
import Group from "./Group";

const data = {
  expression: ["a", "b"],
  groups: [
    {
      expression: [],
      groups: [{ expression: ["c", "d"], groups: null }]
    },
    {
      expression: [],
      groups: [
        { expression: ["e", "f"], groups: null },
        { expression: ["g", "h"], groups: null }
      ]
    },
    {
      expression: ["p"],
      groups: null
    }
  ]
};
const stage = 1;
const Main = () => {
  return (
    <div>
      <Group data={data} stage={stage} />
    </div>
  );
};

export default Main;

Group.js For rendering a group.

import React from "react";
import Expression from "./Expressions";
import Groups from "./Groups";

const populateGroup = (data, stage) => {
  const HTML = [];
  for (const x in data) {
    if (x === "expression") {
      HTML.push(<Expression expression={data[x]} />);
    }
    if (x === "groups") {
      HTML.push(<Groups data={data[x]} stage={stage} />);
    }
  }
  return HTML;
};

const Group = ({ data, stage }) => {
  return (
    <div>
      <p>{`Group @ Stage ${stage}`}</p>
      <ol className="Group">{populateGroup(data, stage + 1).map(el => el)}</ol>
    </div>
  );
};

export default Group;

Expression.js For simply rendering the expression in a group.

import React from "react";

const Expression = ({ expression }) => {

return (
    <div className="expression">
      {expression.map(term => (
        <h3 style={{ display: "inline" }}>{term}</h3>
      ))}
    </div>
  );
};

export default Expression;

Groups.js For extracting and then rendering Group component.

import React from "react";
import Group from "./Group";

const Groups = ({ data, stage }) => {

  if (data === null) return <span></span>;

  return (
    <div className="GroupsSection">
      {data.map((section, i) => (
        <Group key={i} data={section} stage={stage} />
      ))}
    </div>
  );
};

export default Groups;

Performance With this Component-Based Approach the data is being chunked which should improve the performance for even better results the for...in and map can be replaced with for loop. Sample Render

m.sohail
  • 96
  • 1
  • 6
1

Your map() callback doesn't have a return value, yet within the second if statement, you're returning the array, so you'll end up with [undefined, undefined, ...] when you recurse to each groups.

To fix your solution, change both your .map() to .forEach() and remove the return keyword before conditionObj.groups.map(...).

Here's a better approach though:

function ConditionGroup ({ expression, groups }) {
  const conditionExpressions = useMemo(
    () => expression && expression.map(
      (value, index) => (
        <NewExpression key={index} conditionExpression={value} />
      )
    ),
    [expression]
  );
  const conditionGroups = useMemo(
    () => groups && groups.map(
      (value, index) => (
        <ConditionGroup key={index} {...value} />
      )
    ),
    [groups]
  );

  return (
    <div>
      {conditionExpressions}
      {conditionGroups}
    </div>
  );
}

Note that using index as the key is an antipattern. Ideally, you'll want to use a unique id if you're provided one. See ReactJS : What is the best way to give keys in array element for more information on this.

Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
  • Hi, I attempted your method using 'useMemo' and experienced the following error - Uncaught TypeError: nextCreate is not a function at mountMemo (react-dom.development.js?61bb:13459) – Saikat Bhattacharya Oct 21 '19 at 05:41
  • I fixed that issue, useMemo(()=> , []) - this should be the correct syntax for useMemo. But thanks a lot. Your solution works perfectly for our use case. – Saikat Bhattacharya Oct 21 '19 at 06:57
  • @SaikatBhattacharya thanks for pointing that out, I fixed my example now. – Patrick Roberts Oct 21 '19 at 07:17
  • @Patrik I am facing one issue after implementing this - `conditionExpressions` is actually a form with one input field and one dropdown. For the `conditionExpressions` nested in group, whenever I am trying to type in input box it is loosing focus after each letter I type. Any idea what could be the issue? – Saikat Bhattacharya Nov 14 '19 at 06:25