Home > Software engineering >  React loop through nested object
React loop through nested object

Time:08-02

I'm fetching data from strapi. The response for my navigation object looks like that (simplified):

[
   {
      "id":1,
      "title":"Home",
      "order":1,
      "items":[
         {
            "id":2,
            "title":"3D Assets",
            "order":1,
            "items":[
            ]
         },
         {
            "id":4,
            "title":"3D Plants",
            "order":2,
            "items":[
            ]
         },
         {
            "id":3,
            "title":"Surfaces",
            "order":3,
            "items":[
               {
                  "id":5,
                  "title":"Asphalt",
                  "order":1,
                  "items":[
                  ]
               }
            ]
         }
      ]
   },
   {
      "id":6,
      "title":"Collections",
      "order":2,
      "items":[
      ],
      "icon":""
   }
]

Actually I'm looping through my navigation like that:

{Object.entries(navigationItems).map(([key, value]) => {
    return(
        <div className="nav_item">
            <div className="nav_item_parent">{value.title}
            {Object.entries(value.items).map(([key, value]) => {
                    return(
                        <div className="nav_item_child">{value.title}
                            {Object.entries(value.items).map(([key, value]) => {
                                    return(
                                        <div className="nav_item_child">{value.title}</div>
                                    )
                            })}                                         
                        </div>
                    )
            })} 
            </div>
        </div>
    )
})}

enter image description here

How can I create a navigation without repeating the code for each child? (Because the object could be nested many times)

CodePudding user response:

Here just placing some demo code , please have reference and implement as per your need Parent Component

    import React, {Children} from 'react';

function recursionExample(props) {
  let data = [
    {
      id: 1,
      title: 'Home',
      order: 1,
      items: [
        {
          id: 2,
          title: '3D Assets',
          order: 1,
          items: [],
        },
        {
          id: 4,
          title: '3D Plants',
          order: 2,
          items: [],
        },
        {
          id: 3,
          title: 'Surfaces',
          order: 3,
          items: [
            {
              id: 5,
              title: 'Asphalt',
              order: 1,
              items: [],
            },
          ],
        },
      ],
    },
    {
      id: 6,
      title: 'Collections',
      order: 2,
      items: [],
      icon: '',
    },
  ];
  return (
    <div>
      {data.map((item, index) => {
        return (
          <>
            <div>{item.title}</div>
            {item.items && <ChildrenCom data={item.items}></ChildrenCom>}
          </>
        );
      })}
    </div>
  );
}

export default recursionExample;

Now below component will call till last-child , as it is called recursively

import React from 'react';

function ChildrenCom(props) {
  let {data} = props;
  return (
    <div>
      {data.map((item, index) => {
        return (
          <>
            <div>{item.title}</div>
            {item.items && <ChildrenCom data={item.items}></ChildrenCom>}
          </>
        );
      })}
    </div>
  );
}

export default ChildrenCom;

CodePudding user response:

We could use Depth First Traversal to help us avoid duplication. If you're not comfortable with Depth First Traversal or Recursion, I would recommend you to go through the following snippet initially.

function dfs(item, depth = 0) {
    if (!item || Object.keys(item).length === 0) return;

    console.log("\t".repeat(depth), item.title);

    for (const subItem of item.items) {
        dfs(subItem, depth   1);
    }
}

// Consider payload to be the response that you get from the API.
for (const item of payload)  {
    dfs(item)
}

Once you're comfortable, you could translate it into React.

const Nav = ({ item, depth = 0 }) => {
  if (!item || Object.keys(item).length === 0) return;

  return (
    <>
      <p style={{ paddingLeft: `${depth * 64}px` }}>{item.title}</p>
      {item.items.map((subItem, index) => (
        <Nav item={subItem} depth={depth   1} />
      ))}
    </>
  );
};

export default function App() {
  return (
    <div className="App">
      {payload.map((item) => (
        <Nav item={item} />
      ))}
    </div>
  );
}

CodePudding user response:

Just a simple recursive tree walk. A component like this:

const NodePropTypes = PropTypes.objectWithShape({
  id: PropTypes.number,
  title: PropTypes.string,
  items: PropTypes.array,
});
const NavListPropTypes = {
  nodes: PropTypes.arrayOf( NodePropTypes ),
};

function NavList( props ) {
  const nodes = props?.nodes ?? [];
  if (nav.length) {
    return (
      <list>
        <ListItems nodes={nodes} />
      </list>
    );
  }
}
NavList.propTypes = NavListPropTypes

function ListItems( props ) {
  const nodes = props?.nodes ?? [];
  return (
    <>
      { nodes.map( node => <ListItem node={node} /> ) }
    </>
  );
}
ListItems.propTypes = NavListPropTypes;

function ListItem( props ) {
  const node = props?.node ?? {};
  return (
    <li id={node.id} >
      <p> {node.title} </p>
      <NavList nodes={node.items} />
    </li>
  );
}
ListItem.propTypes = NodePropTypes;

which can be rendered passing your navigation response:

<NavList nodes={navigationResponse} />

And should yield something like this:

<list>
  <li id="1" >
    <p> Home </p>
    <list>
      <li id="2" >
        <p> 3D Assets </p>
      </li>
      <li id="4" >
        <p> 3d Plants </p>
      </li>
      <li id="3" >
        <p> Surfaces </p>
        <list>
          <li id="5" >
            <p> Asphalt </p>
          </li>
        </list>
      </li>
    </list>
  </li>
  <li id="6" >
    <p> Collections </p>
  </li>
</list>
  • Related