I am making the interactive table using react-table
on my application.
My goal is to make a table that can be an editable per row when clicking on a button in a table cell.
I designed an EditableCell
like the following.
import React, {useState} from "react";
export const EditableCell = ({
value: initialValue,
row: Row,
column: {id, editable, state},
isEditing,
updateItem, // This is a custom function that we supplied to our table instance
}) => {
// We need to keep and update the state of the cell normally
const [value, setValue] = React.useState(initialValue);
const {index} = Row;
const onChange = e => {
setValue(e.target.value);
};
// We'll only update the external data when the input is blurred
const onBlur = () => {
updateItem(index, id, value);
}
// If the initialValue is changed external, sync it up with our state
React.useEffect(() => {
setValue(initialValue)
}, [initialValue]);
/**
* Event handler to make a row editable.
* @param e
*/
const setRowEditing = (e) => {
// TODO
};
let retObj = null;
if (isEditing && editable) {
switch (id) {
default:
retObj = <input className="input-edit w-100" value={value} onChange={onChange} onBlur={onBlur}/>;
break;
}
} else {
switch (id) {
case 'action_btn':
retObj = <>
<button className="btn btn-sm btn-info btn-sm-td" onClick={setRowEditing}>{ isEditing? "Save" : "Edit"}</button>
</>;
break;
default:
retObj = <div>{value}</div>;
break;
}
}
return retObj;
}
export const defaultColumn = {
Cell: EditableCell,
};
And my table's definition is: (I used my EditableCell component)
export default function Table({ columns, data, updateItem }) {
// Use the useTable Hook to send the columns and data to build the table
const {
getTableProps, // table props from react-table
getTableBodyProps, // table body props from react-table
headerGroups, // headerGroups, if your table has groupings
rows, // rows for the table based on the data passed
prepareRow // Prepare the row (this function needs to be called for each row before getting the row props)
} = useTable({
columns,
data,
defaultColumn,
updateItem,
}, useBlockLayout, useRowState);
/*
Render the UI for your table
- react-table doesn't have UI, it's headless. We just need to put the react-table props from the Hooks, and it will do its magic automatically
*/
return (
<table className="data-table" {...getTableProps()}>
<thead>
{headerGroups.map(headerGroup => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map(column => (
<th {...column.getHeaderProps()}>{column.render("Header")}</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{rows.map((row, i) => {
prepareRow(row);
return (
<tr {...row.getRowProps()}>
{row.cells.map(cell => {
return <td {...cell.getCellProps()}>{cell.render("Cell")}</td>;
})}
</tr>
);
})}
</tbody>
</table>
);
};
In setRowEditing
function, I am going to change the state of the current row or its cells so that cells in the row are rendered as the input field or whatever.
But I don't know how to change the state properly.
Could you advise on this?
In your column array that you pass into react table you need to create a button who's onClick function takes a callback to edit your data to add an isEditing: true
so you will handle turning the row to edit mode from outside of the table. No need for setRowEditing
in an editable cell.
function to set the table data with 'isEditing' property
const handleClickEditRow = (rowIndex) => {
setTableData(prev => prev.map((r, index) => ({...r, isEditing: rowIndex === index})))
}
in your columns
{
accessor: '[editButton]',
Cell: (cellObj) => <button onClick={() => handleClickEditRow(cellObj.row.index)}>Edit</button>
}