1

I've been trying to make it work for days without success. I created a table using React, I added the ability to add a new person to the table, delete them. I want to add the ability to edit the people on the table.

     const [contacts, setContacts] = useContext(Context);
     const [addFormData, setAddFormData] = useState({
firstName: "",
lastName: "",
salary: "",
    });




const handleAddFormChange = (event) => {
     event.preventDefault();

   const fieldName = event.target.name;
const fieldValue = event.target.value;

const newFormData = { ...addFormData };
newFormData[fieldName] = fieldValue;

setAddFormData(newFormData);
 };

const handleAddFormSubmit = (event) => {
const newContact = {
  id: nanoid(),
  firstName: addFormData.firstName,
  lastName: addFormData.lastName,
  salary: addFormData.salary,
  };

const newContacts = [...contacts, newContact];
setContacts(newContacts);

setAddFormData(null); // 👈️ input required to resubmit
setShow(false); // 👈️ closes modal after submit
   };

I'm using the map function to display the content

      {contacts.map((row,index) => (
    <TableRow
      key={index}
      id={index}}
4
  • 1
    You should add the code you've attempted to your question as a minimal reproducible example. Here's some documentation on how to create a React snippet. Commented Sep 30, 2022 at 12:48
  • 1
    yeah 👆 .. There could be many ways it can be done, one of it would be having an icon and on click open a modal, have form fields with table column names with input fields and a submit button, which on submit edits the value (sets state per se), close the modal after submitted. Commented Sep 30, 2022 at 12:50
  • The icon should be for each row, and you can pass all row data to the modal (which is the better UX) or empty the fields (easy but .... ) Commented Sep 30, 2022 at 12:57
  • @KcH I have the idea on how to do it but I can't get it to work. do you have some code to help me ? thanks Commented Sep 30, 2022 at 13:04

2 Answers 2

1

Admitting you want to directly edit your contacts state, you'll most likely need something like this:

const handleContactsUpdate = (updatedContact) => setContacts((oldstate) => oldstate.filter((c) => c.id !== updatedContact.id).concat([updatedContact]))

It should be called by your TableRow whenever that row is edited with the updated contact as param.

Some explanations:

  • setState can be passed a function, with the current state (before setState is applied) as param
  • overall, when editing arrays, it's often easier to filter out the one you're updating then concat the updated object, instead of trying to actually update the array as it

So you basically ask setContacts to set contacts to what it actually is, minus the contact with the same ID than updatedContact, and you concat updatedContact on top of it.

Drawback: it mess your array order, but if you're only using array structure for manipulation, it shouldn't be a problem as you could (and probably should) .sort() before the .map() when rendering for consistency.

Some more edit: you'll also need another function within TableRow to recreate your contact object from all the fields when one of them is edited, and pass that object to the function above

Sign up to request clarification or add additional context in comments.

1 Comment

Hey, thanks a lot for the help ! What other functions should I add in order for it to work ? If you don't mind , can you please write the whole thing if you have time ? thanks
1

Here's a solution that allows you to edit-in-place rather than with a form in a modal. Just hover over the cell you want to edit, and click on it.

const { Fragment, useEffect, useRef, useState } = React;

// Pass in some sample data
function Example({ data }) {

  // Use a reference to indicate which cell is
  // being edited
  const editing = useRef(null);

  // Set up states for the data, and the cell being edited
  const [ rows, setRows ] = useState(data);
  const [ edit, setEdit ] = useState({ row: null, cell: null });

  // When a cell is clicked get its row and cell
  // information from the dataset, and update the
  // edit state
  function handleEdit(e) {
    const { row, cell } = e.target.dataset;
    setEdit({ row: +row, cell: +cell });
  }

  // Called when the edit state is changed - to
  // focus in input on the clicked cell
  function updateFocus() {
    if (editing.current) editing.current.focus();
  }

  // When the edit state is changed, focus the
  // cursor in the input of the clicked cell
  useEffect(() => updateFocus, [edit]);

  // When a cell is updated, copy the state, update
  // the copied state, and set the new state with the copy.
  // Update the edit state
  function handleUpdate(e) {
    if (e.code === 'Enter') {
      const { dataset: { row, cell }, name, value } = e.target;
      const copy = rows.map(obj => ({ ...obj }));
      copy[row][name] = value;
      setRows(copy);
      setEdit({ row: null, cell: null });
    }
  }

  // Build cells from the row data returning either
  // a cell with an input (depends on whether the edit
  // state matches the row/cell information), or just a
  // regular cell
  function buildCells(row, rowId) {
    return Object.entries(row).map(([key, value], cellId) => {
      if (rowId === edit.row && cellId === edit.cell) {
        return (
          <td>
            <input
              ref={editing}
              name={key}
              data-row={rowId}
              placeholder={rows[rowId][key]}
              onKeyDown={handleUpdate}
            />
          </td>
        );
      }
      return (
        <td
          className="normal"
          data-row={rowId}
          data-cell={cellId}
          onClick={handleEdit}
        >{value}
        </td>
      );
    });
  }

  // Build some rows
  function buildRows(rows) {
    return rows.map((row, rowId) => {
      return (
        <tr>
          {buildCells(row, rowId)}
          <td
            data-row={rowId}
            onClick={handleDelete}
            className="delete"
          >X
          </td>
        </tr>
      );
    });
  }
 
  // Remove a row when the delete button
  // is clicked
  function handleDelete(rowId) {
    const copy = rows.map(obj => ({ ...obj }));
    copy.splice(rowId, 1);
    setRows(copy);
  }
 
  // Return the JSX by building some rows
  return (
    <table>
      <tbody>
        {buildRows(rows)}
      </tbody>
    </table>
  );

}

const data = [{ id: 1, name: 'Bob', age: 12 }, { id: 2, name: 'Sam', age: 43 }];

ReactDOM.render(
  <Example data={data} />,
  document.getElementById('react')
);
table { border-collapse: collapse; font-size: 1.4em;}
td { padding: 0.2em 0.4em; width: 100px; border: 1px solid lightgray; }
td input { box-sizing: border-box; width: 100%; display: inline-block; border: none;  margin: 0; padding: 0; height: 100%; font-size: 1em; }
td:hover { cursor: pointer; }
td.normal:hover { background-color: #fffff0;}
td.delete { color: red; }
td.delete:hover { background-color: #ffeeee; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>

1 Comment

Hey, thanks a lot for the reply! But I was wondering how tackle the editing through a modal , I would greatly appreciate your help

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.