Ad

React: Recursively Render Nested Elements

- 1 answer

I apologize in advance for code formatting. I have a data source like this:

var data = {
key1: 'value',
key2: 123,
key3: {
 key30: 'value',
 key31: {
  key310: 123,
  key311: 'hello'
 }
}
}

Using React, I try to convert it into a <ul> like this:

<ul id="data">
<li>key1: value</li>
<li>key2: 123</li>
<li>key3: [Object]
 <ul>
  <li>key30: value</li>
  <li>key31: [Object]
   <ul><li>key310: 123</li>
   <li>key311: hello</li>
   </ul>
  </li>
 </ul>
</li>
</ul>

So far, I've tried:

    React.createClass({
    getInitialState: function() {
     return {lists: [{
      key1: 'value',
      key2: 123,
      key3: {
       key30: 'value',
       key31: {
        key310: 123,
        key311: 'hello'
       }
    }
    }]}
    },
    generateFirst: function(parent, data, tempContainer) {
    var li
    var liHead
    var wrap
    var key
    for (key in data) {
      if (this.getType(data[key]) === 'object') {
        // li head
        liHead = React.DOM.li({
          className: 'token-head'
        }, key + ':' + data[key])

        // line
        li = React.DOM.li({
          className: 'iop'
        }, React.DOM.ul({
          className: 'iok'
        }, this.generateFirst(data[key])))

        // wrap
        wrap = React.DOM.ul({
          className: 'wrap-ul'
        }, li)

        tempContainer.push(liHead)
        tempContainer.push(wrap)
      } else {
        li = React.DOM.li({
          className: 'iol'
        }, key + ':' + data[key])
        tempContainer.push(li)
      }
    }
  },
  renderTokenContent: function(data) {
    var tempContainer = []
    this.generateFirst(data, tempContainer)
    return (<ul>{tempContainer}</ul>)
  },
  render: function() {
    var self = this
    return (<ul className="scroll-helper">
                  { this.state.lists.map(function(data) {
                      return (<li >
                                { self.renderTokenContent(data) }
                              </li>)
                    }) }
                </ul>)
  }
})

It parses the whole tree but it doesn't render the elements in the right container.

Ad

Answer

Here is an example of a nested tree from https://github.com/calitek/ReactPatterns React.14.Common/TreeView. Note that the nested property is children in this case.

import React from 'react';
import lodashGet from 'lodash/object/get';

let TreeRootSty = {lineHeight: '120%'}
let liSty = {listStyleType: 'none'};
let ulSty = {height: 'inherit', WebkitPaddingStart: '16px'};
let ulStyle = {height: 'inherit', WebkitPaddingStart: '16px'};
let iconSty = {marginRight: '10px', width: '16px'};

let nottogglable = {
  color: '#FFF',
  cursor: 'pointer',
  margin: '0 0 0 .8em'
};

let togglable = {
  color: '#815C7C',
  cursor: 'pointer',
  margin: '0'
};

let options = {};

let getTreeNode = function(child, index) {
  return <li key={index} style={liSty}><JTreeViewNode node={child} iconClick={this.props.iconClick} titleClick={this.props.titleClick} /></li>;
};

class JTreeViewNode extends React.Component {
  constructor(props) { super(); }
  iconHandler = () => {
    if (this.props.node.children && this.props.node.children.length > 0) {
      this.props.iconClick(this.props.node);
    } else {
      this.clickHandler();
    }
  };
  clickHandler = () => { this.props.titleClick(this.props.node); };
  render() {
    let titleSty = {color: '#afac87', marginTop: '2px'};
    let childNodes;
    let pSty = nottogglable;

    if (this.props.node.children && this.props.node.children.length > 0) {
      childNodes = this.props.node.children.map(getTreeNode, this);
      titleSty.color = this.props.node.selected ? '#7BB53B' : '#AF90A5';
    } else {
      titleSty.color = this.props.node.selected ? '#b58900' : '#afac87';
    }

    let isClosed = true;
    if (this.props.node.closed != null) isClosed = this.props.node.closed;

    let branch = (
      <ul id='ulStyle' style={ulStyle}>
        {childNodes}
      </ul>
    )
    if (isClosed) branch = null;

    let props = this.props;
    let iconType = lodashGet(props, options.typeName);
    if (iconType == options.icon.sun) iconSty.background = "url('./img/sun.ico') 0/16px no-repeat !important";
    else if (iconType == options.icon.leaf) iconSty.background = "url('./img/leaf.ico') 0/16px no-repeat !important";
    else if (iconType == options.icon.snow) iconSty.background = "url('./img/snow.ico') 0/16px no-repeat !important";
    else iconSty.background = "url('./img/sun.ico') 0/16px no-repeat !important";

    return (
      <div id='TreeNode'>
        <div id='pSty' style={pSty} className='FlexBox'>
          <div id='iconSty' onClick={this.iconHandler} style={iconSty}>&nbsp;</div>
          <div id='titleSty' onClick={this.clickHandler} style={titleSty} >
            {this.props.node.title}
          </div>
        </div>
        {branch}
      </div>
    );
  }
}

export default class JTreeView extends React.Component {
  render() {
    options = this.props.options;
    let childNodes = this.props.data.map(getTreeNode, this);
    return (
      <div id='TreeRootSty' style={TreeRootSty}>
        <ul id='ulSty' style={ulSty}>
            {childNodes}
        </ul>
      </div>
    );
  }
}

Ad
source: stackoverflow.com
Ad