javascript - 在 React 中映射数组时使用了上一次渲染的值
我正在为 React 构建一个自定义 Hook,它会对数组应用过滤(按类别)和搜索。
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 };
当我搜索某些内容时它工作正常,但当搜索未启用时,过滤会落后一拍。我开始通过跟踪数组何时变化来调试这个问题,并遇到了一些奇怪的行为:数据在我开始将数组映射到较小组件之前都能正常工作。
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 日志既相同又是更新后的数组,但传递给组件的 props 却是上一次渲染的数组信息?
这是如何可能的,以及像这样的情况如何可以被解决?
我尝试过实现回调函数以及使用 useEffect 和 useLayout 钩子,但两者都失败了。
编辑:
Baymax 请求 setFilter 实现
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;
解决方案:
由于某种原因,直接将 API 数据传递给结果会导致排序出现错误。
if (!results) {
results = data;
}
在将数据的副本传递给结果后,排序开始起作用。
if (!results) {
results = [...data];
}
这对我来说真的很奇怪,因为在控制台打印时,[...data] 和 data 是相同的。