Mapping several layers of inputed nested data in empty array in reactjs

I’m new in React and I need your help figuring out how to solve the problem that I’ve been trying to solve. I’m trying to make a dynamic table in which I can create new input field of a layer just by clicking a plus button. There are going to be 3 sub layers for a main layer and there might be multiple layers. The part where I’m stuck is I don’t know how to add the data in array(using map()) and track them according to their their parent layer. Also, what I want to focus on is that the array will be empty at first and filled from the UI part. I’ve also uploaded a rough diagram of what I want to make. Please feel free to ask more if you don’t understand my question. I’ve also attached the code that I’ve done so far. Don’t hesitate to tell if the code is wrong at all.
sample

import React, { useState, useEffect } from "react";
const Table = () => {
  const dataFields = [];
  const addField = (parentName, layerName, divReference, data) => {
    switch (parentName) {
      case "Food":
        if (layerName == "FIRST_LAYER") {
          dataFields[dataFields.length] = {
            title: data.title,
            batch: data.batch,
            price: data.price,
          };
        } else if (layerName === "SECOND_LAYER") {
          dataFields.forEach((fields) => {
            if (fields.title === data.immediateParentName) {
              fields.child = {
                title: data.title,
                batch: data.batch,
                price: data.price,
              };
            }
          });
        } else if (layerName === "THIRD_LAYER") {
          dataFields.forEach((fields) => {
            if (fields.child.title === data.immediateParentName) {
              fields.child.child = {
                title: data.title,
                batch: data.batch,
                price: data.price,
              };
            }
          });
        }

        break;
    }

    console.log(dataFields);
  };

  useEffect(() => {
    addField("Food", "FIRST_LAYER", null, {
      title: "Fruit",
      batch: "batch",
      price: "price",
    });

    addField("Food", "SECOND_LAYER", null, {
      title: "Apple",
      batch: "batch",
      price: "price",
      immediateParentName: "Fruit",
    });

    addField("Food", "THIRD_LAYER", null, {
      title: "AppleSauce",
      batch: "batch",
      price: "price",
      immediateParentName: "Apple",
    });
  }, []);

  const [dataField, setDataField] = useState([dataFields]);
  function addFields() {
    setDataField((asset) => {
      return [
        ...asset,
        {
          title: "FIRST_LAYER",
          price: "",
          batch: "",
        },
      ];
    });
  }

  return (
    <div className="m-4">
      <h3 className="text-center">lists</h3>
      <table>
        <thead>
          <tr>
            <th>S.N.</th>
            <th>title</th>
            <th>batch number</th>
            <th>price</th>
            <th></th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>1</td>
            <td>
              Foods{"   "}
              <button
                className="btn btn-success btn-sm"
                onClick={() =>
                  addFields("Food", "FIRST_LAYER", null, {
                    title: "apple",
                    batch: "01",
                    price: "100",
                  })
                }
              >
                +
              </button>
            </td>
          </tr>

          {dataFields.map((data, index) => {
            return (
              <tr key={index}>
                <td>{index + 1}</td>
                <td value={data.parentName}>Food</td>
                <td>
                  <button className="btn btn-success btn-sm">+</button>
                </td>
              </tr>
            );
          })}
        </tbody>
      </table>
    </div>
  );
};

export default Table;

Do you have a repo or an online IDE where we can view this?

Yeah, this is a hard problem, conceptually, especially when you are starting. When I see things like:

        } else if (layerName === "SECOND_LAYER") {

I get nervous. Your code should be flexible enough to figure that out. I don’t want to dig too deeply into your code without seeing a working example. But if I were approaching this…

The first thing I would want to do is figure out what my data structure looks like.

First of all, is there another master category besides “Food”? If not, that is redundant. Let’s assume that there are - giving it a number implies that there can be more.

So, at first I was imagining a data structure like this:

const data = [
  {
    name: 'Food',
    canAdd: true,
    children: [
      {
        name: 'Fruit',
        canAdd: true,
        children: [
          {
            name: 'Apple',
            canAdd: true,
            children: [
              {
                name: 'Apple Sauce',
                batchNumber: 10001,
                price: 2,
              },
            ],
          },
        ],
      },
      {
        name: 'Vegetables',
        canAdd: true,
        children: [
          {
            name: 'Potato',
            batchNumber: 11,
            price: 3,
          },
        ],
      },
    ],
  },
]

Since our data is multi-dimensional in nature, it made sense to do it this way. Then you just need a recursive function to go through it to render it, and render the appropriate buttons with each node.

But maybe that is too complicated. Maybe it should just be a flat array, especially since that is how you are showing it on the screen:

const data = [
  { serialNumber: '1', name: 'Food', canAdd: true, batchNumber: null, price: null },
  { serialNumber: '1.1', name: 'Fruit', canAdd: true, batchNumber: null, price: null },
  { serialNumber: '1.1.1', name: 'Apple', canAdd: true, batchNumber: null, price: null },
  { serialNumber: '1.1.1.1', name: 'Apple Sauce', canAdd: false, batchNumber: 1001, price: 2 },
  { serialNumber: '1.2', name: 'Vegetables', canAdd: true, batchNumber: null, price: null },
  { serialNumber: '1.2.1', name: 'Potato', canAdd: false, batchNumber: 11, price: 3 },
]

Then I can break my work into sections. First figure out how to render this, and the buttons. (Maybe the “canAdd” is from logic, but I can’t see what you want there.) Then figure out how to add in values. Then how to delete them. Then how to edit them. Then how to move them.

With adding them, if you come up with a sort/comparison function for the serial numbers, that should be pretty easy. Keep in mind that you can’t compare them as simple strings - it will break if anything gets into double digits. There are similar issues with comparing semver numbers. I’m not saying this is really hard, just that it will take a little attention. Once you can do that, just append then new record and sort. Alternatively, you could figure out how to insert it, but that will still require some kind of a comparison.

Thank you so much Kevin for giving your suggestion to this. I don’t have any repo or online IDE for this as I’m working locally on this problem. And I just noted that I didn’t actually describe what I want to do in detail. I actually want to create a input table where when a user inputs things they can do so in layers like in the given image example and that input values will be added to the array which is empty to begin with. At first, I thought I could do this but now I feel like this is way beyond what I’m learning now and should not try it.

I don’t know enough about your coding ability, so it is hard to say. But an important skill for any developer is to be able to break problems into small, manageable pieces. Can you do it if it is just a list, like a todo app does?

Part of how you would build from there would depend on how you want the user to interact with the app. I’d want to think all of that out before I start, then start with the simplest version possible and build from there.

1 Like

Thank you for your kind words. I will definitely try to solve this by breaking it into small problems first. And yeah, I did learn to make todo app awhile back. Maybe I need to review on that again and go from there. Thank you once again for taking out your time to give the feedback.