'How do I update the parent JSON from child

I'm writing a React code in which I need to upload a CSV file, on click of a button, Create a new column, once I update a value there, update the JSON passed from parent to child. Currently, I'm able to upload files, create a column, edit the data and see the updated data in the child component.

However, I'm unable to understand how can I send it back to the parent. I should be able to see the updated date when I click Get Final Data button. How can I send back data from child to parent?

Here is my code:

LoadFiles.js (parent)

import { useEffect, useState } from "react";
import RenderTable from "./RenderTable";
import Tab from "@mui/material/Tab";
import TabContext from "@mui/lab/TabContext";
import TabList from "@mui/lab/TabList";
import TabPanel from "@mui/lab/TabPanel";

const Load = () => {
  const [files, setFiles] = useState([]);
  const [allData, setAllData] = useState([]);
  const [isDone, setIsDone] = useState(false);
  const [processingData, setProcessingData] = useState(false);
  const [value, setValue] = useState("1");
  const [csvArray, setCsvArray] = useState([]);

  const handleTabChange = (event, newValue) => {
    setValue(newValue);
  };

  const processCSV = (str, fileName, delim = ",") => {
    const headers = str.slice(0, str.indexOf("\r\n")).split(delim);
    const rows = str.slice(str.indexOf("\r\n") + 1, str.length).split("\r\n");
    let nArray = rows.map((row) => {
      const values = row.split(delim);
      const eachObj = headers.reduce((obj, header, i) => {
        obj[header] = values[i];
        return obj;
      }, {});
      return eachObj;
    });
    createKeys(nArray, fileName);
  };

  const createKeys = async (data, fileName) => {
    const dataToSet = {
      fileName: fileName,
      keys: Object.keys(data.length ? data[0] : {}),
      values: data.filter((o) => Object.keys(o).length)
    };
    console.log(JSON.stringify(dataToSet));
    setAllData((prev) => [...prev, dataToSet]);
  };

  useEffect(() => {
    if (
      (files.length >= 1 && files.length === allData.length && !isDone) ||
      processingData
    ) {
      setIsDone(true);
    }
  }, [allData, isDone, files, processingData]);

  useEffect(() => {
    console.log(JSON.stringify(csvArray));
  }, [csvArray]);

  const handleUpload = (e) => {
    Array.from(files).forEach((file) => {
      const currFile = file;
      const reader = new FileReader();
      reader.onload = (e) => {
        const text = e.target.result;
        processCSV(text, file.name.split(".").slice(0, -1).join("."));
      };
      reader.readAsText(currFile);
    });
  };
  const handleChange = (e) => {
    e.preventDefault();
    setFiles(e.target.files);
  };
  const processData = (e) => {
    var currData = allData;
    setAllData([]);
    currData.forEach((item) => {
      let rowWithSum = item.values.map((item1) => {
        let newCol = item1["Rank"];
        return {
          ...item1,
          "New Col": newCol
        };
      });
      createKeys(rowWithSum, item.fileName);
      setProcessingData(true);
    });
  };

  const getLink = () => {
    console.log(JSON.stringify(allData));
  };

  return (
    <div id="formWrapper">
      <div id="centerContent">
        <form id="csv-form">
          <input
            type="file"
            accept=".csv"
            id="csvFile"
            multiple
            onChange={(e) => handleChange(e)}
          />
          <button
            onClick={(e) => {
              e.preventDefault();
              handleUpload();
            }}
          >
            Upload your csv
          </button>
        </form>
        <button
          className="processData"
          onClick={(e) => {
            e.preventDefault();
            processData();
          }}
        >
          Processdata
        </button>
        <button
          onClick={(e) => {
            e.preventDefault();
            getLink();
          }}
        >
          Get Final Data
        </button>
      </div>
      {isDone && allData.length >= 1 && (
        <TabContext value={value}>
          <TabList onChange={handleTabChange}>
            {files.length >= 1 &&
              [...allData].map((item1, idx) => (
                <Tab label={item1.fileName} value={`${idx + 1}`} />
              ))}
          </TabList>
          {files.length >= 1 &&
            [...allData].map((item1, idx) => (
              <TabPanel value={`${idx + 1}`}>
                <RenderTable
                  keys={item1.keys}
                  csvArray={item1.values}
                  setCsvArray={setCsvArray}
                ></RenderTable>
              </TabPanel>
            ))}
        </TabContext>
      )}
    </div>
  );
};

export default Load;

RenderTable.js (child)

import Table from "@mui/material/Table";
import TableBody from "@mui/material/TableBody";
import TableCell from "@mui/material/TableCell";
import TableContainer from "@mui/material/TableContainer";
import TableHead from "@mui/material/TableHead";
import TableRow from "@mui/material/TableRow";
import Paper from "@mui/material/Paper";
import { useEffect, useState } from "react";

const RenderTable = ({ csvArray, setCsvArray, keys }) => {
  const [csvArrayState, setCsvArrayState] = useState(() => csvArray);

  const handleBlur = (e) => {
    e.currentTarget.classList.add("noBorder");
    e.currentTarget.classList.remove("border");
    setCsvArray(csvArrayState);
  };

  useEffect(() => {
    setCsvArrayState(csvArray);
  }, [csvArray]);

  const handleChange = (e) => {
    e.preventDefault();
    e.currentTarget.readOnly = false;
    e.currentTarget.classList.remove("noBorder");
    e.currentTarget.classList.add("border");
  };

  return (
    <TableContainer component={Paper}>
      <Table sx={{ minWidth: 650 }} aria-label="simple table">
        <TableHead>
          <TableRow>
            {keys.map((item, idx) => (
              <TableCell key={idx}>{item}</TableCell>
            ))}
          </TableRow>
        </TableHead>
        <TableBody>
          {csvArray.map((item, i) => (
            <TableRow>
              {keys.map((key, idx) =>
                key === "New Col" ? (
                  <TableCell>
                    <input
                      class="noBorder"
                      type="true"
                      readOnly="true"
                      value={csvArrayState[i]["New Col"]}
                      onChange={(e) => {
                        const updatedRankFields = csvArrayState.map(
                          (r, rIdx) => {
                            if (rIdx === i) {
                              return { ...r, "New Col": e.target.value };
                            } else {
                              return r;
                            }
                          }
                        );
                        setCsvArrayState(updatedRankFields);
                      }}
                      onBlur={(e) => handleBlur(e)}
                      onClick={(e) => handleChange(e)}
                    ></input>
                  </TableCell>
                ) : (
                  <TableCell> {item[key]} </TableCell>
                )
              )}
            </TableRow>
          ))}
        </TableBody>
      </Table>
    </TableContainer>
  );
};

export default RenderTable;

How can I do this? Here is codesandbox link of the same. I've added the files under /Files.



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source