Неперехваченная ошибка диапазона: максимальный размер стека вызовов превышен с помощью динамической рекурсивной функции просмотра дерева

#reactjs #recursion

#reactjs #рекурсия

Вопрос:

Я пытаюсь создать динамический просмотр дерева с помощью Reactjs и material UI, но когда у меня есть уровни рекурсии, я получил ошибку Uncaught RangeError: Maximum call stack size exceeded в коде TreeItem

вам необходимо рассмотреть понимание этой части {item.sub amp;amp; TreeItems(item.sub)}

когда у элемента есть sub, это возвращает a <TreeItem в противном случае он ничего не делает

также обратите внимание, что я создал это представление дерева без рекурсии, и оно работало очень весело, вот так

  <TreeView
        disableSelection
        defaultCollapseIcon={<ExpandMoreIcon />}
        defaultExpandIcon={<ChevronRightIcon />}
      >
        <TreeItem nodeId="1" label="1">
          <TreeItem nodeId="12" label="12">
            <TreeItem nodeId="3" label="3">
              <TreeItem nodeId="13" label="13"></TreeItem>
            </TreeItem>
          </TreeItem>
        </TreeItem>
      </TreeView>
  

моя структура набора данных

 const myData = [
  { id: 1, text: "value" },
  {
    id: 2,
    text: "xxx",
    sub: [
      {
        id: 10,
        text: "value",
        sub: [
          {
            id: 100,
            text: "value",
            sub: [{ id: 20, text: "value", sub: [{ id: 200, text: "value" }] }],
          },
        ],
      },
    ],
  },
];
  

моя рекурсивная функция

 function TreeItems(myData) {
    return myData.map((item, index) => (
      <TreeItem
        onLabelClick={(e) => e.preventDefault()}
        key={index}
        nodeId={`${index}`}
        label={
          <div
            style={{ display: "flex" }}
            draggable={isDraggable}
            {...Draggable(item, index, Update, setDraggable)}
          >
            <DragIndicatorIcon
              onMouseDown={(e) => setDraggable(true)}
              onMouseUp={(e) => setDraggable(false)}
            />
            <Text>{item.text}</Text>
          </div>
        }
      >
        {item.sub amp;amp; TreeItems(item.sub)}
      </TreeItem>
    ));
  }
  


примечание: вероятно, нет необходимости видеть мой полный, но на всякий случай.
мой полный код
app.tsx

 import React from "react";
import "./App.css";
import Tree from "./Components/Tree";
// import { DataGrid } from "@material-ui/data-grid";
import RouterBreadcrumbs from "./Components/Nav";
import { BrowserRouter, Route } from "react-router-dom";
import SimpleBreadcrumbs from "./Components/BreadCrumbs";

const My = [
  {
    id: 0,
    text: (
      <div>
        this is an example <b>data</b>
      </div>
    ),
  },
  { id: 1, text: "test tow" },
  { id: 2, text: "test three" },
  {
    id: 3,
    text: "main",
    sub: [
      { id: 4, text: "sub 1" },
      { id: 5, text: "sub 2" },
    ],
  },
];
const Home = [
  { id: 1, text: "value" },
  {
    id: 2,
    text: "xxx",
    sub: [
      {
        id: 10,
        text: "value",
        sub: [
          {
            id: 100,
            text: "value",
            sub: [{ id: 20, text: "value", sub: [{ id: 200, text: "value" }] }],
          },
        ],
      },
    ],
  },
];
function App() {
  return (
    <BrowserRouter>
      <div className="App">
        <SimpleBreadcrumbs />
        <RouterBreadcrumbs />
        <Route exact path="/Home" component={(e: any) => Tree(Home)} />
        <Route exact path="/My" component={(e: any) => Tree(My)} />
      </div>
    </BrowserRouter>
  );
}

export default App;
export function my(x: string) {
  return x.toUpperCase();
}
  

tree.tsx

 import React, { useReducer, useState } from "react";
import TreeView from "@material-ui/lab/TreeView";
import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
import ChevronRightIcon from "@material-ui/icons/ChevronRight";
import TreeItem from "@material-ui/lab/TreeItem";
import Draggable from "../Functions/Draggable";
import DragIndicatorIcon from "@material-ui/icons/DragIndicator";
import Text from "./Text";

function deleting(state, action) {
  state.forEach((item) => {
    if (item.id === action.i.item.id) {
      state.splice(action.i.index, 1);
    } else if (item.id !== action.i.item.id amp;amp; item.sub) {
      deleting(item.sub, action);
    }
  });
  return state;
}
function adding(state, action) {
  state.forEach((item) => {
    if (item.id === action.item.id) {
      state.splice(action.f, 0, action.i.item);
    } else if (item.sub) {
      adding(item.sub, action);
    }
  });
  return state;
}
function reordering(state, action) {
  const newState = deleting(state, action);
  const newState2 = adding(newState, action);
  return newState2;
}
function subbing(state, action) {
  state.forEach((item) => {
    if (item.id === action.item.id) {
      if (item.sub) {
        Object.assign(item.sub, [...item.sub, action.i.item]);
      }
      if (!item.sub) {
        Object.assign(item, { sub: [action.i.item] });
        console.log(state);
      }
    } else if (item.sub) {
      console.log("infient");
      subbing(item.sub, action);
    }
  });
  return state;
}
function Tree(myData) {
  const [isDraggable, setDraggable] = useState(false);
  const [state, setData] = useState(myData);
  console.log(state);
  function Update(item, i, f, p) {
    const action = { item: item, i: i, f: f, p: p };

    setData((pre) => {
      if (p === "on") {
        const newPre = deleting(pre, action);
        const newPre2 = subbing(newPre, action);
        return newPre2;
      } else {
        const newPre = deleting(pre, action);
        const newPre2 = reordering(newPre, action);
        return newPre2;
      }
    });
  }

  function TreeItems(state) {
    return state.map((item, index) => (
      <TreeItem
        onLabelClick={(e) => e.preventDefault()}
        key={index}
        nodeId={`${index}`}
        label={
          <div
            style={{ display: "flex" }}
            draggable={isDraggable}
            {...Draggable(item, index, Update, setDraggable)}
          >
            <DragIndicatorIcon
              onMouseDown={(e) => setDraggable(true)}
              onMouseUp={(e) => setDraggable(false)}
            />
            <Text>{item.text}</Text>
          </div>
        }
      >
        {item.sub amp;amp; TreeItems(item.sub)}
      </TreeItem>
    ));
  }
  return (
    <div>
      <TreeView
        disableSelection
        defaultCollapseIcon={<ExpandMoreIcon />}
        defaultExpandIcon={<ChevronRightIcon />}
      >
        {TreeItems(state)}
      </TreeView>
    </div>
  );
}

export default Tree;