2
votes

I have 2 sub classes and 1 super class (3 components):

  1. Navigation (super)
  2. TopNavZone
  3. MobileNavZone

export default class Navigation extends Component {

  constructor(props) {
    super(props);
    this.state = {
      navItems: [],
      navMenu: []
   };
 }

 fetchNavItems(clientId) {
   NavMenuAPI.getAll(clientId)
     .then((response) => {
       const data = response.data;
       this.setState({ navItems: data });
       this.showNavMenu();
     });
   }
 }

Each sub class calls the fetch method in componentDidMount, and then the fetch call, after getting the data, calls the respective sub class's showMenu method:


export default class TopNavZone extends Navigation {

  constructor(props) {
      super(props);
  }

  componentDidMount() {
    const clientId = this.props.clientId;
    // in super class
    this.fetchNavItems(clientId);
 }

 showNavMenu() {
   const navItems = this.state.navItems;
   const results = [];
   navItems.map((item, index) => {
    results.push(

     // whatever html markup belongs here for TopNavZone

   );
 });
   this.setState({ navMenu: results });
}

 render() {
   if (!this.state.navMenu) return <div>Loading</div>;
   return (
    <div>{ this.state.navMenu }</div>
   )
}

I know what the error message is telling me. I know React no longer allows objects to be rendered as a child. I tried ...

React.addons.createFragment(results) 

in the showNavMenu and received the error that cannot create fragment of undefined.

I like as much of my html away from the render section and refactored into the respective functions that deal with it, so I really do not want to load up my render section with the showNavMenu markup. I'd just assume call it in one line from the render section.

What must I do to make this work and keep a tidy render section?

Many Thanks!

1

1 Answers

1
votes

I found the solution and have kept the render section tidy.

The key to my solution lies in performing the mapping inside the render and NOT calling a function to perform that same said mapping.

So .......

  1. Remove the this.showNavMenu() from the super class' fetchNavItems, and the navMenu array from state.

  2. Render now looks like this:

    render() {
      if (!this.state.navItems) return <div>Loading ...</div>;
      return (
        <section>
          <nav>
            <ul>
               { this.state.navItems.map(this.showNavMenu.bind(this)) }
           </ul>
         </nav>
      </section>
     );
    }
    
  3. showNavMenu has changed to:

    showNavMenu(item) {
      const results = [];
      let subMenu = [];
      let childrenLength = 0;
      if (item.children !== undefined) {
        const children = item.children;
        childrenLength = children.length;
        subMenu = this.fetchSubMenu(children);
      }
      results.push(
        <li key={ item.index }>
            { item.title }
          </Link>
          { subMenu }
        </li>
      );
      return results;
    }
    
  4. fetchSubMenu:

     fetchSubMenu(children) {
      const results = [];
      children.map((child, idx) => {
        results.push(
          <div key={ idx }>
              <Link to={ child.linkTo }>
                { child.title }
              </Link>
          </div>
        );
      });
      return results;
    }