I’ve built a react app in which I’m fetching api data and displaying on UI, in each row I’m adding editing and deleting the row data locally (not overwriting the real data from API, just in local storage). Editing is inline functionality.
My node version is 18.12.1 and npm version is 10.2.3
here is my app.js file:
import React, { useState, useEffect } from 'react';
import Table from './table';
import './App.css';
import axios from 'axios';
//import { AppStateProvider } from './AppStateContext';
const API_ENDPOINT = 'https://geektrust.s3-ap-southeast-1.amazonaws.com/adminui-problem/members.json';
const App = () => {
const [data, setData] = useState([]);
useEffect(() => {
// Fetch data from the API
async function fun() {
const res = await axios.get(API_ENDPOINT);
setData(res.data);
}
fun();
}, []);
return (
<div>
<Table data={data} />
</div>
);
};
export default App;
here is table.js component:
import React, { useState, useEffect } from 'react';
import TableRow from './tableRow';
import './table.css';
const Table = ({ data }) => {
const [searchTerm, setSearchTerm] = useState('');
const [selectedRows, setSelectedRows] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
const [isEditing, setEditing] = useState(false);
const [editedRowData, setEditedRowData] = useState({});
const rowsPerPage = 5;
// Use the data state to manage the original data
const [visibleData, setVisibleData] = useState(data);
useEffect(() => {
const filteredData = data
.filter(row =>
Object.values(row).some(value =>
value.toString().toLowerCase().includes(searchTerm.toLowerCase())
)
);
setVisibleData(filteredData.slice((currentPage - 1) * rowsPerPage, currentPage * rowsPerPage));
}, [data, searchTerm, currentPage, rowsPerPage]);
const totalPages = Math.ceil(data.length / rowsPerPage);
const paginationButtons = Array.from({ length: totalPages }, (_, index) => index + 1);
const handleEdit = (editedRow) => {
setEditedRowData(editedRow);
setEditing(true);
};
const handleSave = () => {
// Update the data state to reflect changes
const updatedData = data.map(row => (row.id === editedRowData.id ? editedRowData : row));
// Update the visibleData state to reflect changes and pagination
setVisibleData(updatedData.slice((currentPage - 1) * rowsPerPage, currentPage * rowsPerPage));
setEditing(false);
};
const handleDelete = () => {
// Update the data state to reflect changes
const updatedData = data.filter(row => !selectedRows.includes(row.id));
// Update the visibleData state to reflect changes and pagination
setVisibleData(updatedData.slice((currentPage - 1) * rowsPerPage, currentPage * rowsPerPage));
setSelectedRows([]);
};
const handlePageChange = (page) => {
setCurrentPage(page);
};
const handleToggleAllRows = () => {
const allRows = visibleData.map(row => row.id);
if (selectedRows.length === allRows.length) {
setSelectedRows([]);
} else {
setSelectedRows(allRows);
}
};
return (
<div>
<input
type="text"
placeholder="Search..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
<table>
<thead>
<tr>
<th>
<input
type="checkbox"
checked={selectedRows.length === visibleData.length}
onChange={handleToggleAllRows}
/>
</th>
{Object.keys(data[0] || {}).map((column) => (
<th key={column}>{column}</th>
))}
<th>Edit</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
{visibleData.map((row) => (
<TableRow
key={row.id}
row={row}
selectedRows={selectedRows}
setSelectedRows={setSelectedRows}
isEditing={isEditing}
onEdit={handleEdit}
onSave={handleSave}
/>
))}
</tbody>
</table>
<div className="pagination">
{paginationButtons.map((button) => (
<button
key={button}
onClick={() => handlePageChange(button)}
className={button === currentPage ? 'active' : ''}
>
{button}
</button>
))}
</div>
<button className="delete-selected" onClick={handleDelete}>
Delete Selected
</button>
</div>
);
};
export default Table;
and this is tableRow.js:-
import React, { useState } from 'react';
const TableRow = ({ row, selectedRows, setSelectedRows, isEditing, onEdit, onSave }) => {
const [editedValues, setEditedValues] = useState({ ...row });
const isSelected = selectedRows.includes(row.id);
const handleEdit = (tempRow) => {
onEdit(tempRow);
};
const handleSave = () => {
onSave();
};
const handleToggleRow = () => {
setSelectedRows(prevSelectedRows => {
if (prevSelectedRows.includes(row.id)) {
return prevSelectedRows.filter(id => id !== row.id);
} else {
return [...prevSelectedRows, row.id];
}
});
};
const handleChange = (column, value) => {
setEditedValues(prevEditedValues => ({
...prevEditedValues,
[column]: value,
}));
console.log("edited values:",editedValues);
};
return (
<tr className={isSelected ? 'selected' : ''}>
<td>
<input
type="checkbox"
checked={isSelected}
onChange={handleToggleRow}
/>
</td>
{Object.keys(row).map(column => (
<td key={column}>
{isEditing ? (
<input
type="text"
value={editedValues[column]}
onChange={(e) => handleChange(column, e.target.value)}
/>
) : (
row[column]
)}
</td>
))}
<td>
{isEditing ? (
<button onClick={() => handleSave(editedValues)}>Save</button>
) : (
<button onClick={() => handleEdit(editedValues)}>Edit</button>
)}
</td>
<td>
{!isEditing ? (
<button className="delete" onClick={() => setSelectedRows([row.id])}>
Delete
</button>
) : null}
</td>
</tr>
);
};
export default TableRow;
can someone help me fix this issue? I’m not getting why is this happening?
Upon clicking edit button getting an editable row and upon saving the edited row it should reflect on the UI. But here I need to click twice on edit and save to get what I wanted. like once goto edit, change data and save, again open edit and save, and then only the changes are reflected on UI. I’m not getting why I need to save it twice to get the reflected changes. Also on refreshing the page the original data from API should display and i.e. working fine in my app.
I’ve tried using chatGPT but couldn’t solve the problem.