javascriptreactjsjsxuser-inputreact-state

How to re-render table to reflect input data


I have two files/components:

The problem is Tablepage renders only the attributes from Createrisk which are not dependent on user input i.e the ID and a Test attribute. I believe this makes sense, on first render attributes tied to inputs are empty.

How do I get the Tablepage component to re-render? Ideally tied to this event on Createrisk:

<button onClick={handleSubmission}>Store my data</button>

I have a vague idea I need a useState, but I am unsure how to do this across different components (JSX files).

Createrisk.jsx:

function Createrisk() {
  function handleSubmission() {
    var risks = JSON.parse(localStorage.getItem("allrisks"));
    if (risks == null) {
      risks = [];
    }
    let riskitem = {
      riskname: document.getElementById("rname").value,
      riskdesc: document.getElementById("rdesc").value,
      riskimpact: document.getElementById("rimpact").value,
      test: "test",
    };
    risks.push(riskitem);
    let i = 1;
    risks.map((n) => {
      n["id"] = i;
      i++;
    });

    localStorage.setItem("allrisks", JSON.stringify(risks));
  }

  return (
    <div>
      <input type="text" id="rname" placeholder="Risk Name" />
      <input type="text" id="rdesc" placeholder="Risk Description" />
      <input type="text" id="rimpact" placeholder="Risk Impact" />
      <button onClick={handleSubmission}>Store my data</button>
    </div>
  );
}
export default Createrisk;

Tablepage.jsx:

function Tablepage() {
  const risksfromstorage = JSON.parse(localStorage.getItem("allrisks"));

  const columns = [
    { field: "id", headerName: "ID", width: 350 },
    { field: "rname", headerName: "Risk Name", width: 350 },
    { field: "rdesc", headerName: "Risk Desc", width: 350 },
    { field: "rimpact", headerName: "Risk Impact", width: 350 },
    { field: "test", headerName: "test", width: 350 },
  ];

  const rows = risksfromstorage.map((row) => ({
    id: row.id,
    rname: row.rname,
    rdesc: row.rdesc,
    rimpact: row.rimpact,
    test: row.test,
  }));

  return (
    <div>
      <DataGrid getRowId={(row) => row.id} columns={columns} rows={rows} />
    </div>
  );
}

export default Tablepage;

As one can see, the Array/Object gets added to localstorage. Only the non-input attributes are populated in the table (ID & Test)

Img showing Table and LocalStorage


Solution

  • It appears that the "allrisks" data value stored in localStorage is your source of truth. Instead of attempting to work with localStorage directly, create a state in a common ancestor component that is initialized from localStorage and pass the state down as props to these two components. When the state is updated this will trigger a React component re-render.

    Example:

    const CommonAncestor = () => {
      // Lazy initialize state from localStorage
      const [allRisks, setAllRisks] = React.useState(() => {
        return JSON.parse(localStorage.getItem("allrisks")) || [];
      });
    
      // Side-effect to persist state to localStorage
      React.useEffect(() => {
        localStorage.setItem("allrisks", JSON.stringify(allRisks));
      }, [allRisks]);
    
      // Handler to update state
      const addRisk = (risk) => {
        setAllRisks(allRisks => allRisks.concat(risk));
      };
    
      return (
        <>
          ...
          <CreateRisk addRisk={addRisk} />
          ...
          <TablePage rows={allRisks} />
          ...
        </>
      );
    };
    

    CreateRisk.jsx

    import { nanoid } from 'nanoid';
    
    function CreateRisk({ addRisk }) {
      function handleSubmission() {
        addRisk({
          id: nanoid(),
          riskname: document.getElementById("rname").value,
          riskdesc: document.getElementById("rdesc").value,
          riskimpact: document.getElementById("rimpact").value,
          test: "test",
        });
      }
    
      return (
        <div>
          <input type="text" id="rname" placeholder="Risk Name" />
          <input type="text" id="rdesc" placeholder="Risk Description" />
          <input type="text" id="rimpact" placeholder="Risk Impact" />
          <button onClick={handleSubmission}>Store my data</button>
        </div>
      );
    }
    

    TablePage.jsx

    const columns = [
      { field: "id", headerName: "ID", width: 350 },
      { field: "rname", headerName: "Risk Name", width: 350 },
      { field: "rdesc", headerName: "Risk Desc", width: 350 },
      { field: "rimpact", headerName: "Risk Impact", width: 350 },
      { field: "test", headerName: "test", width: 350 },
    ];
    
    function TablePage({ rows }) {
      return (
        <div>
          <DataGrid
            getRowId={(row) => row.id}
            columns={columns}
            rows={rows}
          />
        </div>
      );
    }