0

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)

1
  • create a component and call it recursively , till the array is not finish Commented Aug 1, 2022 at 17:13

3 Answers 3

1

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;
Sign up to request clarification or add additional context in comments.

Comments

1

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>
  );
}

Comments

1

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>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.