1

Using React context-menu, I am trying todynamically create a menu tree with submenus. I'm able to render the menu successfully, but I´m not able to get submenu working (see error below code).

import React, { Component } from 'react';
import ContextMenuTrigger from 'src/ContextMenuTrigger';
import ContextMenu from 'src/ContextMenu';
import MenuItem from 'src/MenuItem';
import SubMenu from 'src/SubMenu';

const MENU_TYPE = 'SIMPLE';

const targets= [
{
    name: 'Banana', 
}, 
{
    name: 'Apple', 
    subname: [
        {
        value: 'Red Apple', 
        description: 'description for red apple'
        },
        {
        value: 'Green Apple', 
        description: 'description for green apple'
        }
    ]
},
{
    name: 'Grapes', 
},
{
    name: 'Orange', 
    subname: [
        {
        value: 'Orange Juice', 
        description: 'description for Orange'
        },
        {
        value: 'Orange Color', 
        description: 'description for Orange'
        }
    ]
}
];



export default class SimpleMenu extends Component {
  constructor(props) {
    super(props);
}

render() {
    return (
        <div>
            <ContextMenuTrigger id={MENU_TYPE}>
                <div className='well'>right click menu</div>
            </ContextMenuTrigger>

            <ContextMenu id={MENU_TYPE}>                    
                {targets.map((item) => {
                            return (
                                <MenuItem>
                                    {item.name}
                                    <SubMenu>
                                    {item.subname.map(action => {
                                        {action.value}
                                    })}
                                    </SubMenu>
                                </MenuItem>
                            )
                        })}
            </ContextMenu>
        </div>
    );
 }
}

I´m getting error on this line:

 item.subname.map(action => {{action.value}})}                                                   

error: TypeError: item.subname is undefined

Is there a better way to map through the nested object?

The objective is to build the menu and submenu from the object dynamically.

3 Answers 3

2

I think you want item.subname (not targets.subname) and then you want to add an existence check because not all your items have a subname field.

Update with existence check:

{item.subname && item.subname.map()}
Sign up to request clarification or add additional context in comments.

1 Comment

I updated the code to item.subname because that's the code I was testing, but still got "TypeError: item.subname is undefined", the submenu only needs to show for the ones that have nested object, so this is the confusing part that I´m getting stuck, how can I do the existence check and still show the menus with and without submenus.
1

Added to Colin's response (he's saying you are really looking for item.subname in that context, which looks more appropriate than your target.subname.map since you're inside a function that contains item in its scope), make sure your component handles cases where item.subname is undefined; You could do

item.subname != undefined ? 'render submenu items if any' : 'do nothing if undefined';

2 Comments

I end up pasting the targets.subname as typo, I´m also using item.subname, but I reached the part that if my object does not have any nested ojects it breaks, TypeError: item.subname is undefined this part of the code you mentioned to check for underfined is bit confusing, this is in the map function?
@bernlt Sorry for the confusing part. Just add a check for undefined subname before trying to map it. I'm on my phone right now, so to make it simpler for you just add an if(item.subname) around the <SubMenu> {item.subname.map(action => { {action.value} })} </SubMenu> because the way your code is written right now it tries to do a .map on item.subname for an item that has no .subname, hence the TypeError item.subname is undefined. Let me know if is still broken and I'll help you further from my computer.
0

There're a few snags and gotchas that are causing you issues, plus a more robust approach you can take:

  • Optional Chaining: Quite a recent addition, but it means you can write foo?.bar?.bing instead of foo && foo.bar && foo.bar.bing
  • Simplified map: You had too many nested braces { and parentheses ( so I have simplified this to get just action?.value. This would have caused the arrow function to not have an implicit return (see code below), and without an explicit one you will see nothing.
  • Nullish Coalescing: I've put a 'default' in, by using action?.value ?? 'No Action Value' which means if action?.value is null or undefined you get 'No Action Value'. This is different to || in that action?.value can be falsey (e.g. '' (empty string) or 0, etc) and it won't default.

Here is how it looks now:

return (
  <div>
    <ContextMenuTrigger id={MENU_TYPE}>
      <div className='well'>right click menu</div>
    </ContextMenuTrigger>
    <ContextMenu id={MENU_TYPE}>                    
      {targets.map((item) => {
        return (
          <MenuItem>
            {item?.name}
            <SubMenu>
              {item?.subname?.map(action => action?.value ?? 'No Action Value')}
            </SubMenu>
          </MenuItem>
         )
      })}
    </ContextMenu>
  </div>
);

One thing to note, is that your 'targets' array has no sub-items with an action.value property, so I don't know how they are being added.

The more robust approach can be to create a separate component for submenus, and recursively render them. That isn't without its own issues, but in simple cases it can help. This is just an illustrative example (it won't work without big changes):

const RecursiveSubmenu = ({item, Component}) => (
  <Component>
    {item?.name}
    <SubMenu>
      {item?.subname?.map(subname=> RecursiveSubmenu({item: subname, Component: SubMenu})}
    </SubMenu>
  </Component>
)

Or something like that, bearing in mind you may need to change your targets data to work with this approach.

1 Comment

maybe you should highlight what was actually wrong with the asker's attempt to cause the error message?

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.