javascript - Kartläggning av en array i React använder värden från föregående render
Jag bygger en anpassad hook för React, som tillämpar filter (efter kategori) och sökning på en array.
const amountSmallestFirst = (a, b) => {
return a.amount - b.amount;
};
const amountLargestFirst = (a, b) => {
return b.amount - a.amount;
};
const alphabeticallySorted = (a, b) => {
if (a.name < b.name) {
return -1;
}
if (a.name > b.name) {
return 1;
}
return 0;
};
const alphabeticallySortedReverse = (a, b) => {
if (a.name > b.name) {
return -1;
}
if (a.name < b.name) {
return 1;
}
return 0;
};
const useSearchAndFilter = (data: any, isLoading: boolean, sortBy: SortBy) => {
const [search, setSearch] = useState<string>('');
const [currentFilter, setFilter] = useState<SortBy>(sortBy);
const [result, setResult] = useState<any>([]);
const handleSearch = (event: React.FormEvent<HTMLInputElement>) => {
const target = event.target as HTMLInputElement;
setSearch(target.value);
};
const selectSorting = (sortBy = SortBy.AMOUNT_LARGEST_FIRST) => {
switch (sortBy) {
case 'AMOUNT_SMALLEST_FIRST':
return amountSmallestFirst;
case 'AMOUNT_LARGEST_FIRST':
return amountLargestFirst;
case 'ALPHABETICALLY_SORTED':
return alphabeticallySorted;
case 'ALPHABETICALLY_SORTED_REVERSE':
return alphabeticallySortedReverse;
default:
return amountLargestFirst;
}
};
const applySearchAndFilter = (data: any, sortBy = SortBy.AMOUNT_LARGEST_FIRST) => {
if (!data) return null;
let results;
if (search !== '') {
results = data.filter(({ name }: Name) => name.toLowerCase().includes(search.toLowerCase()));
}
const isNumber = parseInt(search);
if (isNumber) {
results = data.filter(({ amount }: Name) => amount === isNumber);
}
if (!results) {
results = data;
}
results.sort(selectSorting(sortBy));
setResult(results);
};
useLayoutEffect(() => {
applySearchAndFilter(data, currentFilter);
}, [isLoading, search, currentFilter]);
return { result, search, setFilter, handleSearch, currentFilter };
};
export { useSearchAndFilter };
Det fungerar bra när jag söker efter något, men när sökfunktionen inte är aktiverad fungerar filtreringen ett steg efter. Jag började felsöka problemet genom att spåra när arrayen ändras och stötte på ett konstigt beteende: datan fungerar tills jag börjar mappa arrayen till mindre komponenter.
console.log(nameList);
const mapListItems = nameList.map((item: Name, i: number) => {
console.log(item, nameList);
return <ListItem key={item.name} item={item} color={i % 2 === 0 ? 'secondary' : 'primary'} />;
});
nameList-loggarna är både identiska och den uppdaterade arrayen, men de props som skickas till komponenten är information från föregående renders array?
Hur är detta möjligt och hur kan situationer som denna lösas?
Jag har försökt att implementera callback-funktioner och useEffect- och useLayout-hooks, men båda misslyckades.
Redigera :
Baymax begärde på setFilter-implementeringen
import React, { useState, useEffect, useLayoutEffect } from 'react';
import { ReactComponent as OpenIcon } from '../assets/open.svg';
import { SortBy } from '../types';
import Togglable from './Togglable';
interface FilterProps {
setFilter: any;
currentFilter: SortBy;
}
const Filter: React.FC<FilterProps> = ({ setFilter }) => {
const [show, setShow] = useState<boolean>(false);
const [current, setCurrent] = useState<SortBy | null>(SortBy.AMOUNT_LARGEST_FIRST);
useLayoutEffect(() => {
setFilter(current);
setShow(false);
}, [current]);
const handleFilterChange = (value: SortBy) => {
setCurrent(value);
};
return (
// Relative parent needed for the togglable selection which is absolute
<div className='relative'>
<div
className='m-w-30 h-11 px-3 bg-gray-800 text-white flex rounded-lg justify-between items-center flex-nowrap space-x-2 cursor-pointer'
onClick={() => setShow(!show)}
>
<p className='flex-none'>Filter by</p>
<OpenIcon className={`w-3 fill-current transition duration-300 linear text-white ${show ? 'arrowDown' : 'arrowUp'}`} />
<Togglable show={show}>
<ul className='bg-gray-800 absolute text-gray-300 -h-full mt-6 w-full -m-5 z-10 rounded-lg sm:w-48 sm:-ml-28'>
<li
className={`p-2 hover:text-white rounded-t-lg ${current === SortBy.AMOUNT_LARGEST_FIRST && 'bg-gray-700'} `}
onClick={() => handleFilterChange(SortBy.AMOUNT_LARGEST_FIRST)}
>
Highest amount first
</li>
<li
className={`p-2 hover:text-white ${current === SortBy.AMOUNT_SMALLEST_FIRST && 'bg-gray-700'}`}
onClick={() => handleFilterChange(SortBy.AMOUNT_SMALLEST_FIRST)}
>
Lowest amount first
</li>
<li
className={`p-2 hover:text-white ${current === SortBy.ALPHABETICALLY_SORTED && 'bg-gray-700'}`}
onClick={() => handleFilterChange(SortBy.ALPHABETICALLY_SORTED)}
>
Alphabetically sorted
</li>
<li
className={`p-2 hover:text-white rounded-b-lg ${current === SortBy.ALPHABETICALLY_SORTED_REVERSE && 'bg-gray-700'}`}
onClick={() => handleFilterChange(SortBy.ALPHABETICALLY_SORTED_REVERSE)}
>
Alphabetically reversed
</li>
</ul>
</Togglable>
</div>
</div>
);
};
export default Filter;
Lösning:
Av någon anledning ledde att skicka API-data direkt till resultaten till ett fel i sorteringen.
if (!results) {
results = data;
}
Efter att ha skickat en kopia av datan till resultaten började sorteringen fungera.
if (!results) {
results = [...data];
}
Detta är verkligen konstigt för mig, eftersom [...data] och data är identiska när de loggas i konsolen.