Languages
[Edit]
EN

React - create dynamic editable table (like spreadsheet)

10 points
Created by:
Lani-Skinner
748

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

Simple dynamic editable table in React (like spreadsheet).
Simple dynamic editable table in React (like spreadsheet).

Practical example:

// ONLINE-RUNNER:browser;

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

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

const itemStyle = {
  	position: 'relative',
    height: '26px'
};

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

const Cell = React.memo(({ value, onChange }) => {
    const valueRef = React.useRef();
  	const inputRef = React.useRef();
  	React.useEffect(() => {
        var input = inputRef.current;
        if (input) {
            input.value = value ?? '';
        }
    }, [value]);
    const handleFocus = () => {
        var input = inputRef.current;
        if (input) {
            valueRef.current = input.value;
        }
    };
	const handleBlur = () => {
        if (onChange) {
            var input = inputRef.current;
            if (input && input.value !== valueRef.current) {
                onChange(input.value);
            }
        }
    };
    return (
        <div style={itemStyle}>
          <input
            ref={inputRef}
            style={inputStyle}
            type="text"
            onFocus={handleFocus}
            onBlur={handleBlur}
          />
        </div>
    );
});

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

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

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

const Row = React.memo(({ 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 
                    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 handleAddClick = () => {
      	onAdd?.(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 = () => {
                      onDelete?.(rowIndex, rowData);
                  };
                  return (
                      <Row 
                        key={rowData[id]}
                        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

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.
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