Languages
[Edit]
EN

React - create dynamic editable table

9 points
Created by:
Adnaan-Robin
724

In this short article, we would like to show how to create simple dynamic editable table in React.

Simple dynamic editable table in React.
Simple dynamic editable table in React.

Note: react this article to see standalone editable table cell example.

Practical example:

// ONLINE-RUNNER:browser;

// Note: Uncomment import lines in your project.
// import React from 'react';
// import ReactDOM from 'react-dom';

// CELL -----------------------------------------

const itemStyle = {
  	padding: '0px',
    position: 'relative',
    height: '26px',
    display: 'flex',
    alignItems: 'center'
};

const textStyle = {
  	...itemStyle,
  	padding: '0px 4px',
  	height: '26px',
  	fontFamily: 'Arial',
  	fontSize: '13px'
};

const inputStyle = {
  	padding: '0',
    position: 'absolute',
    left: '2px',
    top: '2px',
    right: '45px',
    bottom: '2px',
  	minWidth: '20px',
  	fontFamily: 'Arial',
  	fontSize: '13px'
};

const buttonStyle = {
    position: 'absolute',
    top: '2px',
    right: '2px',
    bottom: '2px',
    width: '40px'
};

const Cell = React.memo(({value, mode, onChange}) => {
  	const [localMode, setLocalMode] = React.useState(mode ?? 'read');
  	const [localValue, setLocalValue] = React.useState(value ?? '');
  	React.useEffect(() => setLocalMode(mode ?? 'read'), [mode]);
  	React.useEffect(() => setLocalValue(value ?? ''), [value]);
	if (localMode === 'edit') {
        const handleInputChange = (e) => setLocalValue(e.target.value);
        const handleSaveClick = () => {
            setLocalMode('read');
          	onChange?.(localValue);
        };
        return (
            <div style={itemStyle}>
              <input type="text"
                value={localValue}
                style={inputStyle}
                onChange={handleInputChange} />
              <button style={buttonStyle} onClick={handleSaveClick}>Ok</button>
            </div>
        );
    }
  	if (localMode === 'read') {
        const handleEditClick = () => {
            setLocalMode('edit');
        };
        return (
            <div style={textStyle} onClick={handleEditClick}>{localValue}</div>
        );
    }
  	return null;
});

// ROW ------------------------------------------

const tdStyle = {
  	padding: '1px',
  	border: '1px solid black',
};

const optionStyle = {
  	...tdStyle,
    padding: '2px 2px',
  	width: '30px'
};

const Row = React.memo(({ mode, columns, data, onChange, onDelete}) => {
	const handleDeleteClick = () => onDelete?.();
    return (
        <tr>
          {columns.map(({path}, columnIndex) => {
              const handleChange = value => {
                  if (onChange) {
                      const changedData = { ...data, [path]: value };
                      onChange(columnIndex, changedData);
                  }
              };
              return (
                  <td key={path} style={tdStyle}>
                    <Cell 
                      mode={mode}
                      value={data[path]} 
                      onChange={handleChange} 
                    />
                  </td>
              );
          })}
          <td style={optionStyle}>
            <button onClick={handleDeleteClick}>Delete</button>
          </td>
        </tr>
    );
});

// TABLE ----------------------------------------

const tableStyle = {
    border: '1px solid black',
    borderCollapse: 'collapse',
  	width: '100%'
}

const Table = React.memo(({id, columns, data, onAdd, onChange, onDelete}) => {
    const [addedIndex, setAddedIndex] = React.useState();
  	const handleAddClick = () => {
    	onAdd?.(data.length);
      	setAddedIndex(data.length);
    };
	return (
        <div>
          <table style={tableStyle}>
            <tbody>
              <tr>
                {columns.map(({path, name}) => (
                  <th key={path} style={tdStyle}>{name}</th>
                ))}
              </tr>
              {data.map((rowData, rowIndex) => {
                  const handleChange = (columnIndex, changedData) => {
                      onChange?.(rowIndex, columnIndex, changedData);
                  };
                  const handleDelete = () => {
                      if (rowIndex !== addedIndex) {
                          setAddedIndex(addedIndex - 1);
                      }
                      onDelete?.(rowIndex, rowData);
                  };
                  return (
                      <Row 
                        key={rowData[id]}
                        mode={addedIndex === rowIndex ? 'edit' : 'read'}
                        columns={columns}
                        data={rowData}
                        onChange={handleChange}
                        onDelete={handleDelete}
                      />
                  );
              })}
            </tbody>
          </table>
          <br />
          <div>
            <button onClick={handleAddClick}>Add row</button>
          </div>
        </div>
    );
});

// UTILS ----------------------------------------

// https://dirask.com/snippets/React-append-prepend-remove-and-replace-items-in-array-with-utils-for-useState-D7XEop

const appendItem = (updater, item) => {
    updater(array => array.concat(item));
};
  
const replaceItem = (updater, index, item) => {
    updater(array => array.map((value, i) => i === index ? item : value));
};

const deleteItem = (updater, index) => {
    updater(array => array.filter((value, i) => i !== index));
};



// Example --------------------------------------

const columns = [
  	{ path: 'id',   name: 'ID'   },
    { path: 'name', name: 'Name' },
    { path: 'age',  name: 'Age'  }
];

let counter = 0;

const App = () => {
  	const [data, setData] = React.useState(() => ([
        { id: ++counter, name: 'Bob',  age: 22 },
        { id: ++counter, name: 'Adam', age: 43 },
        { id: ++counter, name: 'Mark', age: 16 },
        { id: ++counter, name: 'John', age: 29 }
	]));
  	const handleAdd = (rowIndex) => {
      	const newRowData =  { id: ++counter };
        appendItem(setData, newRowData);
      	//TODO: AJAX request to server
      	console.log(`Added empty row!`);
    };
  	const handleChange = (rowIndex, columnIndex, changedRowData) => {
        replaceItem(setData, rowIndex, changedRowData);
    	const changedRowJson = JSON.stringify(changedRowData, null, 4);
      	//TODO: AJAX request to server
      	console.log(`Changed row:\n${changedRowJson}`);
    };
  	const handleDelete = (rowIndex, deletedRowData) => {
        deleteItem(setData, rowIndex);
      	//TODO: AJAX request to server
      	console.log(`Deleted row: ${rowIndex}`);
    };
    return (
        <div>
          <Table
            id="id"
            columns={columns}
            data={data}
            onAdd={handleAdd}
            onChange={handleChange}
            onDelete={handleDelete}
          />
        </div >
    );
};

const root = document.querySelector('#root');
ReactDOM.render(<App/>, root );

 

See also

  1. React - create dynamic editable table (like spreadsheet) 
Donate to Dirask
Our content is created by volunteers - like Wikipedia. If you think, the things we do are good, donate us. Thanks!
Join to our subscribers to be up to date with content, news and offers.

ReactJS

React - editable table
Native Advertising
🚀
Get your tech brand or product in front of software developers.
For more information Contact us
Dirask - we help you to
solve coding problems.
Ask question.

❤️💻 🙂

Join