React JS Filters being overwritten by setState data in useEffect

Advertisements

I have a table where I’m setting data inside a useEffect from an api. My filter logic iterates through the "rows" variable which is being set inside this useEffect. However, every-time the user searches via an input which has an onChange event the useEffect setRows I believe is setting the data over and over again.

What would be a better way to set the data so it doesn’t conflict with my filtering logic?

//State

const [documents, setDocuments] = useState<IDocument[]>([]);
const [rows, setRows] = useState<Data[]>([]);

//useEffect to setData

useEffect(() => {
//setDocuments from claimStore when component mounts
    setDocuments(claimsStore.getIncomingDocuments());

    //setRows from documents when component mounts
    setRows(
        documents.map((document) =>
        createData(
            document.documentAuthor ?? '',
            document.documentMetadataId.toLocaleString(),
            document.documentMetadataId.toLocaleString(),
            document.documentName ?? '',
            document.documentSource ?? '',
            document.documentType,
            document.featureId ?? '',
            document.mimeType,
            document.uploadDateTime,
        ),
        ),
    ); 
}, [claimsStore, documents]);

//Filter logic that updates rows as user input values captured

const filterBySearch = (e: React.ChangeEvent<HTMLInputElement>) => {
    const { name, value } = e.target;
    const newFilters = { ...filters, [name]: value };
    //Update filters with user input
    setFilters(newFilters);

    //Filter documents based on user input
    const updatedList = rows.filter((document) => {
        return (
        document.documentAuthor.toLowerCase().includes(filters.documentAuthor.toLowerCase()) &&
        document.documentName.toLowerCase().includes(filters.documentName.toLowerCase()) &&
        document.documentSource.toLowerCase().includes(filters.documentSource.toLowerCase()) &&
        document.documentType.includes(filters.documentType === 'All' ? '' : filters.documentType) &&
        document.featureId.includes(filters.featureId) 
        );
    });

    //Trigger render with updated values
    setRows(updatedList);
};

Use of filterBySearch:

<TableCell align={'center'} className={classes.tableCell}>
    <input
    value={filters.featureId}
    onChange={(e) => filterBySearch(e)}
    name="featureId"
    className={classes.inputCell}
    />
</TableCell>   

>Solution :

This is one of the things useMemo is good for: Have an array of filtered rows, that you update as necessary when rows or filters changes:

const [documents, setDocuments] = useState<IDocument[]>([]);
const [rows, setRows] = useState<Data[]>([]);
// ...
const filteredRows = useMemo(
    () => rows.filter((document) => (
        document.documentAuthor.toLowerCase().includes(filters.documentAuthor.toLowerCase()) &&
        document.documentName.toLowerCase().includes(filters.documentName.toLowerCase()) &&
        document.documentSource.toLowerCase().includes(filters.documentSource.toLowerCase()) &&
        document.documentType.includes(filters.documentType === 'All' ? '' : filters.documentType) &&
        document.featureId.includes(filters.featureId) 
    )),
    [rows, filters]
);

Then filterBySearch is just:

const filterBySearch = (e: React.ChangeEvent<HTMLInputElement>) => {
    const { name, value } = e.target;
    const newFilters = { ...filters, [name]: value };
    //Update filters with user input
    setFilters(newFilters);
};

Leave a ReplyCancel reply