Home > Back-end >  Loop through JSON in JSX
Loop through JSON in JSX

Time:07-05

I have a tree like JSON structure and it can be n levels deep. Here is an example:

"plot": {
  "population": "All",
  "gates": [
    {
      "name": "population1",
      "plot": {
        "population": "population1",
        "gates": [
          {
            "name": "population3",
            "plot": {
              "population": "population3",
            }
          }
        ]
      }
    },
    {
      "name": "population2",
      "plot": {
        "population": "population4",
      }
    }
  ]
};

It starts with plot. The is the top level. A plot can have many gates. These are essentially branches. Each gate has another plot, which can have multiple gates etc.

I want to output the plot.population within JSX wrapped in a div. Here is my attempt (MultiStainState is a JSON file with the above JSON):

function Plot(props) {

  ...

  const renderPlots = (plotObject) => {
    console.log("plotObject is ", plotObject);
    if (plotObject) {
      return (
        <>
          <div>{plotObject.population}</div>
        </>
      );
    }

    {
      plotObject.gates.map((gate, gateIndex) => {
        plotObject(gate.plot);
      });
    }
  };


  return (
      <div
        style={{
          height: "200px",
        }}
      >
        Render Plots:
        {renderPlots(MultiStainState)}
      </div>
 );
}

This output Render Plots:All and none of the child plot populations.

This is presumably because of the return within renderPlots(). I feel like I need to use recursion here (as I have attempted to do). But I cant figure out how....

CodePudding user response:

Here is a simple rendering of a recursive component based on this article. It checks if it has a gates array of length > 0 and if so will recursively render the component
sandbox

const plot = {
  "population": "All",
  "gates": [
    {
      "name": "population1",
      "plot": {
        "population": "population1",
        "gates": [
          {
            "name": "population3",
            "plot": {
              "population": "population3",
            }
          }
        ]
      }
    },
    {
      "name": "population2",
      "plot": {
        "population": "population4",
      }
    }
  ]
}

const RecursiveComponent = ({ population, gates }) => {
  const hasGates = gates && gates.length

  return (
    <React.Fragment>
    <div>
      {population}
    </div>
    {hasGates && gates.map(({name,plot}) => <RecursiveComponent key={name} population={plot.population} gates={plot.gates} />)}
    </React.Fragment>
  );
};


const App = props => {
  return (
     <RecursiveComponent population={plot.population} gates={plot.gates} /> //starting point
  );
};



ReactDOM.render(<App />, document.getElementById("app"));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="app"></div>

CodePudding user response:

The main issue with your renderPlots function is that if the given plotObject is non-null, the function just returns plotObject.population and never gets to the recursive step. There are some other issues with that function, but I'm going to offer a rewrite that will address those.

I'm not sure of your exact desired output format, but I'm going to use nesting <div> elements so that the DOM hierarchy matches the JSON structure.

You'll want to return a single JSX object with the recursive step within (recursion looks a bit weird in React/JSX compared to usual programming). I've also split the renderPlots function into a separate component, but that's more of a stylistic choice (I'll leave you to find a better name for the component).

Here's a simple example:

function PlotRender({ plotObject }) {
    if (!plotObject) {
        return null; // just in case
    }

    return (
        <div>
            {plotObject.population}
            {plotObject.gates?.map(e => (
                <PlotRender key={e.name} plotObject={e.plot}/>
            ))}
        </div>
    );
}

Which will render as (for the given sample data):

<div>
    All
    <div>
        population1
        <div>
            population3
        </div>
    </div>
    <div>
        population4
    </div>
</div>

Note also that in the outer <Plot> component, you'll likely need to pass MultiStainState.plot as the plotObject prop to <PlotRender> rather than just MultiStainState.

  • Related