Как я могу создать древовидную таблицу вне структуры, используя вложенный json, используя React

Привет всем, у меня есть проблема или более того, я хочу знать: «Как я могу создать древовидную таблицу вне структуры, используя вложенный json». Без использования какой-либо третьей части lib/package

У меня есть вложенный json, как показано ниже:

[
  {
    "id": 1,
    "name": "VehicleData",
    "owner": "admin",
    "sub_details": [
      {
        "sub_name": "Lexus",
        "sub_id": 4,
      },
      {
        "sub_name": "BMW",
        "sub_id": 3,
      }
    ]
  },
  {
    "id": 2,
    "name": "Mobiles,
    "owner": "admin",
    "sub_details": [
      {
        "sub_name": "Apple",
        "sub_id": 2,
      }
    ]
  },
  {
    "id": 3,
    "name": "Laptop",
    "owner": "admin",
    "sub_details": []
  }
]

Что я пробовал: -

import React, {useState, useEffect} from 'react';
import classes from './TableData.module.css';

const TableData = () => {
    const [oldData, newData] = useState([]);

    useEffect(() => {
        fetchData();
    }, []);

    const Id = (dataId) => {
        alert(dataId);
    };
    const Name = (dataName) => {
        alert(dataName);
    };

    const fetchData = async () => {
        try {
            const response = await fetch('someapi/json');
            const data = await response.json();
            newData(data);
        } catch (e) {
            console.warn(e)
        }
    }


    const DisplayData = oldData.map((data) => {
        return (
            <tr key = {
                data.id
            }>
                <td> {
                    data.id
                } </td>
                <td> {
                    data.name
                } </td>
                <td> {
                    data.owner
                } </td>
                <td>
                    <button onClick = {
                        () => Id(data.id)
                    }>getId</button>
                    <button onClick = {
                        () => Name(data.name)
                    }>getname</button>
                </td>
            </tr>
        )
    })


    return (
        <div>
            <div>
                <table>
                    <thead>
                        <tr>
                            <th>id</th>
                            <th>name</th>
                            <th>owner</th>
                        </tr>
                    </thead>
                    <tbody> {DisplayData} </tbody>
                </table>

            </div>


        </div>
    )

}

export default TableData;

что я наделал : Я создал таблицу с помощью json, а также добавил кнопку для получения родительских данных.

что мне нужно и как я могу сделать:

  1. Как я могу добавить создание вложенной структуры без использования сторонних плагинов, а также добавить кнопку в каждую строку.

  2. Как я могу получить родительские данные и дочерние данные при нажатии кнопки каждой дочерней строки.

  3. Как я могу переключать и держать открытой вложенную структуру.

если какой-либо URL-адрес информации есть, это будет здорово?

🤔 А знаете ли вы, что...
JavaScript активно развивается, и новые версии языка регулярно включают новые функции и улучшения.


2
143
2

Ответы:

Решено

В React вы должны разбивать свои данные на компоненты. Работа с данными как есть в многоуровневой вложенности считается плохим дизайном — подробное объяснение смотрите здесь.

Имея это в виду, я выделил по одному компоненту для каждого уровня вложенности. Таким образом, строка может управлять скрытым состоянием своей подстроки. Вот окончательный код — см. полную демонстрацию здесь — нажмите кнопку «переключить»:

index.js:

import React, { useState, useEffect } from "react";
import "./styles.css"

const Table = () => {
  const [items, setItems] = useState([]);

  useEffect(() => {
    fetchData();
  }, []);

  const fetchData = async () => {
    try {
      const response = await fetch("./data.json");
      const data = await response.json();
      setItems(data);
    } catch (e) {
      console.warn(e);
    }
  };
  
  return <div>
    {
      items.map((data) => { 
        return <TableData key = {data.id} data = {data} />
      })
    }
  </div>;
};

const TableData = (props) => {
  const data = props.data;
  const [subHidden, setSubHidden] = useState(false);
  
  const Id = (dataId) => {
    alert(dataId);
  };
  const Name = (dataName) => {
    alert(dataName);
  };
  const Toggle = () => {
    setSubHidden(state => { 
      return !state;
    });
  };

  return (
    <div >
      <span> {data.id} </span>
      <span> {data.name} </span>
      <span> {data.owner} </span>
      <span>
        <button onClick = {() => Id(data.id)}>getId</button>
        <button onClick = {() => Name(data.name)}>getname</button>
        <button onClick = {() => Toggle()}>toggle</button>
      </span>
      <TableSubDetails 
        subDetails = {data.sub_details}
        style = {{ display: subHidden ? 'none' : 'block'}}
      />
    </div>
  );
}

const TableSubDetails = (props) => {
  const subDetails = props.subDetails;
  return (
    <div className = "subData" style = {props.style}>
      {
        subDetails.map((subData) => { 
          return <div key = {subData.sub_id}>
            <span> {subData.sub_id} </span>
            <span> {subData.sub_name} </span>
          </div>
        })
      }
    </div>
  );
}

export default Table;

стили.css:

.subData {
  padding: 5px 15px;
}  

Вот моя версия решения, обновив существующий код некоторой логикой.

const TableData = () => {
  const [oldData, newData] = useState([]);
  const [id, setId] = useState({});

  useEffect(() => {
    fetchData();
  }, []);

  const Id = (dataId) => {
    alert(dataId);
  };
  const Name = (dataName) => {
    alert(dataName);
  };

  const fetchData = async () => {
    try {
      newData(jsonData);
    } catch (e) {
      console.warn(e);
    }
  };

  const updateId = useCallback((dataId) => {
    setId((prevState) => {
      let updatedState = { ...prevState };
      const hasId = updatedState[dataId] ? true : false;

      if (hasId) {
        delete updatedState[dataId];
      } else {
        updatedState = { ...updatedState, [dataId]: dataId };
      }

      return {
        ...updatedState
      };
    });
  }, []);

  const renderTableRows = useCallback(
    (details, isChild = false) => {
      let renderData = [];

      details.forEach((detail) => {
        const detailId = isChild ? detail["sub_id"] : detail["id"];
        const name = isChild ? detail["sub_name"] : detail["name"];

        const childData = (
          <tr key = {`${name}-${detailId}`}>
            <td> {detailId} </td>
            <td> {name} </td>
            <td> {detail.owner} </td>
            <td>
              <button onClick = {() => Id(detailId)}>getId</button>
              <button onClick = {() => Name(name)}>getname</button>
              {detail.sub_details?.length > 0 && (
                <button
                  onClick = {() => {
                    updateId(detailId);
                  }}
                >
                  {id[detailId] === detailId ? "Hide" : "Show"} child
                </button>
              )}
            </td>
          </tr>
        );

        renderData.push(childData);

        if (id[detailId] === detailId && detail.sub_details?.length > 0) {
          const childData = renderTableRows(detail.sub_details, true);
          renderData = [...renderData, ...childData];
        }
      });

      return renderData;
    },
    [id, updateId]
  );

  const DisplayData = useMemo(() => {
    return renderTableRows(oldData);
  }, [oldData, renderTableRows]);

  return (
    <div>
      <div>
        <table>
          <thead>
            <tr>
              <th>id</th>
              <th>name</th>
              <th>owner</th>
            </tr>
          </thead>
          <tbody> {DisplayData} </tbody>
        </table>
      </div>
    </div>
  );
};

export default TableData;

вот пример ссылка.

Спасибо