Как визуализировать компонент React в себе, рекурсивным образом

У меня есть компонент, скажем, List Component. Компонент List может отображать элементы через компонент ListItem. Который может быть легко достигнут с чем-то вроде ниже

import React from 'react';
import ListItem from '../list-item/list-item';

class List extends React.Component {

  renderListItems() {
    return this.props.items.map(item => <ListItem item={item} />)
  }

  render(){
    return (
      <div className="list">{this.renderListItems()}</div>
    )
  }

}

а также

import React from 'react';
class ListItem extends React.Component {

  render(){
    return (
      <div className="list-item">Name: {this.props.item.name}</div>
    )
  }

}

Теперь, в этом случае, если элементы списка могут иметь родительские дочерние элементы внутри них, то один элемент списка может дополнительно отобразить больше элементов списка, как я могу визуализировать компонент ListItem из самого себя. Ниже приводится дерево рендеринга, которое я ищу, а также состояние моих данных

List
 - ListItem
  - ListItem
  - ListItem
 - ListItem
 - ListItem

редактировать

Я думаю, что правильная структура будет чем-то вроде этого

List
 - ListItem
  - List
    - ListItem
    - ListItem
 - ListItem
 - ListItem

Но это, конечно же, создаст круговую зависимость между компонентами List и ListItem, будет ли это проблемой?

Ответ 1

Вы должны абстрагировать логику рендеринга в отдельный метод, который вы можете вызывать рекурсивно из метода "рендеринга":

import List from 'somewhere';

class RecursiveItems extends React.PureComponent {
  constructListItem = (item) => {
    if (item.nestedItems) {
      return (
        <List key={item.key}>
          {item.nestedItems.map(this.constructListItem)}
        </List>
      )
    } else {
      return <ListItem key={item.key} item={item} />
    }
  }

  render() {
    const { listItems } = this.props

    return (
      <List>
        {listItems.map(this.constructListItem)}
      </List>
    )
  }
}

export default RecursiveItems

Полностью непроверенный код, но он должен дать идею.

Ответ 2

Использование функциональных компонентов:

//List.js
import React from 'react';

import ListItem from '../your_path/ListItem';

export default function List({items}) {
  return(
    <div className="list">
    {
      items.map(item => {
        if(item.name){
          return <ListItem item={item} />
        }
        return <List items={item} /> 
      })
    }
    </div>
  );
}


//ListItem.js: 
import React from 'react';
export default function ListItem({item}){
  return (
    <div className="list-item">Name: {item.name}</div>
  )
}

Не проверял, но я думаю, что это должно работать..

Ответ 3

Таким образом, рекурсия чище

const Asterics= ({i}) => [
  <b>*</b>,
  i>0 && <RAsterics i={i-1}/>
]
const RAsterics = (props)=><Asterics {...props}/>

export default Asterics