This issue seems common enough, but I cannot quite make the connection for my scenario.
I have a table in React that has the ability to search, and I have an Actions column that allows for updating and deleting data. They work functionally with a Spring Boot API and a SQL datbase, but my problem consists in refreshing the view after the deletion happens.
I'm working off of a precedent from here: https://codesandbox.io/s/62brk?file=/src/useTableSearch.js:0-1515
This is my code:
Columns.js:
export const userColumns = [
{
title: "First Name",
dataIndex: "firstName",
key: "firstName"
},
{
title: "LastName",
dataIndex: "lastName",
key: "lastName"
},
{
title: "Pronoun(s)",
dataIndex: "pronoun",
key: "pronoun"
},
{
title: "Date of Birth",
// dataIndex: "birth",
key: "birth",
render: record => {
return Object.values(record.birth.slice(0, 10))
}
},
{
title: "Address",
key: "address",
render: record => {
return Object.values(record.address)
.filter(val => typeof val !== "object")
.join("");
}
},
{
title: "City",
dataIndex: "city",
key: "city"
},
{
title: "Province",
dataIndex: "province",
key: "province"
},
{
title: "Postal Code",
dataIndex: "postalCode",
key: "postalCode"
},
{
title: "Phone",
dataIndex: "phone",
key: "phone"
},
{
title: "Email",
dataIndex: "email",
key: "email"
},
{
title: "Contact Preference",
dataIndex: "contactPref",
key: "contactPref"
},
{
title: "Daytime Preference",
dataIndex: "daytimePref",
key: "daytimePref"
},
{
title: "Actions",
dataIndex: "actions",
key: "actions",
}
];
UseTableSearch.js:
// reference from: https://codesandbox.io/s/62brk?file=/src/useTableSearch.js:0-1515
import { useState, useEffect } from "react";
export const useTableSearch = ({ searchVal, retrieve }) => {
const [filteredData, setFilteredData] = useState([]);
const [origData, setOrigData] = useState([]);
const [searchIndex, setSearchIndex] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
setLoading(true);
const crawl = (user, allValues) => {
if (!allValues) allValues = [];
for (var key in user) {
if (typeof user[key] === "object") crawl(user[key], allValues);
else allValues.push(user[key] + " ");
}
return allValues;
};
const fetchData = async () => {
const { data: users } = await retrieve();
setOrigData(users);
setFilteredData(users);
const searchInd = users.map(user => {
const allValues = crawl(user);
return { allValues: allValues.toString() };
});
setSearchIndex(searchInd);
if (users) setLoading(false);
};
fetchData();
}, [retrieve]);
useEffect(() => {
if (searchVal) {
const reqData = searchIndex.map((user, index) => {
if (user.allValues.toLowerCase().indexOf(searchVal.toLowerCase()) >= 0)
return origData[index];
return null;
});
setFilteredData(
reqData.filter(user => {
if (user) return true;
return false;
})
);
} else setFilteredData(origData);
}, [searchVal, origData, searchIndex]);
return { filteredData, loading };
};
showUsers.js:
// reference from: https://codesandbox.io/s/62brk?file=/src/useTableSearch.js:0-1515
import React, { useState } from "react";
import { Link} from "react-router-dom";
import { Table, Input } from "antd";
import axios from "axios";
import { userColumns} from './Columns'
import { useTableSearch } from "./UseTableSearch";
import '../styling/ShowUsers.css';
import userService from '../services/UserServices';
const { Search } = Input;
// const deleteUser = (id) => {
// userService.deleteUser(id)
//axios.delete(`http://localhost:8080/clients/${index}`)
// .then(res => {
// const users = this.state.users.filter(item=> item.id !== id);
// this.setState({ users });
});
}
// const [users, setUsers] = useState([]); //{ data }
// const [user, setUser] = useState('');
const showUsers = async () => {
const { data } = await axios.get(
"http://localhost:8080/clients/"
) return {data}
}
// .then((res) => {
// setUsers({users: res.data});
// })
// const rows = users.map((item, index) => {
// return (
// <tr>
// <td>
// <Link to={"/editUser/" + user.id} className="btn btn-outline-success my-2 // text-center mr-2">Update User</Link>
// <button className="btn btn-outline-primary text-center mr-2" onClick={() // => {deleteUser(user.id); }} />
// </td>
// </tr>
//
)
// })
// }
export default function App() {
// const [rows, setRows] = useState(dbValues);
// const deleteRow = (number) => {
// let copy = [...rows]
// copy = copy.filter((item, index) => number != index)
// setRows(copy);
// }
// const [users, setUsers] = useState([]);
const [searchVal, setSearchVal] = useState(null);
const { filteredData, loading } = useTableSearch({
searchVal,
retrieve: showUsers
});
return (
<>
<Search id="searchBox"
onChange={e => setSearchVal(e.target.value)}
placeholder="Search"
enterButton
style={{ position: "sticky", top: "0", left: "0" }}
/>
<br /> <br />
<Table
rowKey="name"
dataSource={filteredData}
columns={userColumns}
loading={loading}
pagination={false}
/>
</>
);
}
The approaches I've tried include putting in a render component inside the Actions column in Columns.js. Then I stuck the deleteUser function atop the Columns array, and it works with an Axios request. This approach worked almost entirely, save for the requirement to do a manual refresh on the page to reflect the changed data in the database.
return (
<td>
<Link to={"/editUser/" + user.id} className="btn btn-outline-success my-2 text-center mr-2">Update User</Link>
<button className="btn btn-outline-primary text-center mr-2" onClick={() => {deleteUser(user.id); //RefreshComponent && <RefreshComponent />}}>Delete User</button>
</td>
)
And delete component:
const deleteUser = (id) => {
userService.deleteUser(index)
axios.delete(`http://localhost:8080/clients/${index}`)
}
The limitation with this approach is that I cannot carry out a comparison against the remaining content, which is otherwise a common approach for the response after the deletion:
deleteUser(index) {
console.log(index);
userService.deleteUser(index)
axios.delete(`http://localhost:8080/clients/${index}`)
.then(res => {
const users = this.state.users.filter(item=> item.id !== index);
this.setState({ users });
})
}
Other things I looked at, include this example, which does what I'm looking for, but I can't translate its approach to my code (plus, my code is using Ant Design).
The commented out lines in showUsers.js reflect an attempt to add the buttons separately, outside of the Columns file in a separate return component, but that also went nowhere.
Finally, the use of a useState hook, which would generally work in this case, is not permitted to be used in the Columns file, either outside of it, or within a function within the event trigger for the Delete button, inside the return component for the column.
I've gone through the implementation of a separate Delete Component or a Refresh Component that I can import into Columns (e.g. RefreshComponent && <RefreshComponent />
) and invoke once a deletion happens. Likewise, I've tried to use useNavigate
as a way to invoke another get request and refresh the view, but that also was not possible.
Finally, I've looked at things on SO for a solution, such as this one, and they use a class-based approach, whereas I am generally working with hooks, and I also cannot find a decisive precedent for an example that's built around a search function and a separate Columns import.
Overall, my mind is looping on itself on this problem, and I can't exactly figure out how to implement the view refresh after the delete request passes.
Thank you in advance!
Let me share an idea. One of the ideas behind React is the ability to make UI (from larger UI's to single components) to auto-render or re-render (whatever you prefer) based on changes to a registered state.
So, what you have to achive as a developer is try to register that user data set as state to that component which is in charge of rendering the table.
You can achieve this with a hook useState()
in case you are using a funcional approach or this.setState()
if you are using a class' approach. Or updating the props passed to this component from a higher component (usually a parent).
So your challenge is to be able to update this state for example, when a user is deleted. So if you manage to update the registered dataset once it changes, then the React component, in this case, the table will re-render.