Reputation: 119
I added a search function for my table in React to filter out items fetched from an external webservice. Since I don't want to call the API every time you search for project I figured I'd assign the data retrieved to two different useState hooks.
One to hold the complete dataset and the other to contain the filtered items based on the search.
Could I write cleaner code without using 2 hooks? Any side effects of the way the code is handling this?
Any input is appreciated.
import React, { useState, useEffect } from 'react';
import axios from 'axios';
import Table from '@mui/material/Table';
import TableHead from '@mui/material/TableHead';
import TableBody from '@mui/material/TableBody';
import TableRow from '@mui/material/TableRow';
import TableCell from '@mui/material/TableCell';
import DeleteIcon from '@mui/icons-material/Delete';
import TextField from '@mui/material/TextField';
export default function ShowProject() {
const [data, setData] = useState([]);
const [filter, setFilter] = useState([])
useEffect(() => {
const fetchData = async () => {
const result = await axios(
'http://127.0.0.1:5000/pr'
);
setData(result.data);
setFilter(result.data);
}
fetchData()
}, []);
const requestSearch = (searchedVal) => {
const filteredRows = data.filter((row) => {
return row.customer.toString().toLowerCase().includes(searchedVal.toString().toLowerCase());
});
if (searchedVal.length < 1) {
setFilter(data)
}
else {
setFilter(filteredRows)
}
};
return (
<div>
<div>
<TextField onChange={(e) => requestSearch(e.target.value)} />
<Table>
<TableHead>
<TableRow>
<TableCell>Project</TableCell>
<TableCell>Code</TableCell>
<TableCell>Customer</TableCell>
<TableCell></TableCell>
</TableRow>
</TableHead>
<TableBody>
{filter.map(item => (
<TableRow key={item.db_id}>
<TableCell>{item.project_name}</TableCell>
<TableCell>{item.project_code}</TableCell>
<TableCell>{item.customer}</TableCell>
<TableCell><DeleteIcon /></TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
</div>
)
}
Upvotes: 4
Views: 15631
Reputation: 3075
I don't see any particular reason to use the filter
state variable. You're scanning data
and assigning the filtered result to filter
every time your TextField
changes, so why not just do the filtering directly in the JSX and instead store the query text as state? More concretely, something like the following:
const [searchedVal, setSearchedVal] = useState("");
return (
<div>
<div>
{/* simply set the query text here instead of triggering requestSearch */}
<TextField onChange={(e) => setSearchedVal(e.target.value)} />
<Table>
<TableHead>
<TableRow>
<TableCell>Project</TableCell>
<TableCell>Code</TableCell>
<TableCell>Customer</TableCell>
<TableCell></TableCell>
</TableRow>
</TableHead>
<TableBody>
{data
.filter((row) =>
// note that I've incorporated the searchedVal length check here
!searchedVal.length || row.customer
.toString()
.toLowerCase()
.includes(searchedVal.toString().toLowerCase())
)
.map((item) => (
<TableRow key={item.db_id}>
<TableCell>{item.project_name}</TableCell>
<TableCell>{item.project_code}</TableCell>
<TableCell>{item.customer}</TableCell>
<TableCell>
<DeleteIcon />
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
</div>
);
Upvotes: 4