我正在开发 Nextjs 应用程序。这是我的客户端组件中的内容:
const [view, setView] = useState<string[]>(["all"]);
const story = stories?.holidays;
const joke = stories?.jokes;
const idiom = stories?.idioms;
const factoid = stories?.culturalTips;
const handleClick = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
const target = e.currentTarget as HTMLButtonElement;
const value = target.innerText.toLowerCase();
setView((prevState) => {
if (value === "all") {
return ["all"];
} else {
// If any other filter is clicked while "all" is selected, deselect "all"
if (prevState.includes("all")) {
return [value];
} else {
// For other filters, toggle their selection status
if (prevState.includes(value)) {
const filteredView = prevState.filter(v => v !== value);
//Checks if no filter is selected, if so selects "all"
return filteredView.length === 0 ? ["all"] : filteredView;
} else {
return [...prevState, value];
}
}
}
});
};
return (
<Flex>
<Button
justifyContent={'center'}
backgroundColor={view.includes('all') ? 'gray.200' : 'orange.50'}
textColor={view.includes('all') ? 'black' : 'white'}
disabled={isLoading}
onClick={(e) => handleClick(e)}
>
<Text fontSize={'sm'}>All</Text>
</Button>
<Button
justifyContent={'center'}
backgroundColor={view.includes('story') ? 'gray.200' : 'orange.50'}
textColor={view.includes('story') ? 'black' : 'white'}
disabled={isLoading}
onClick={(e) => handleClick(e)}
>
// the rest of the buttons
</Flex>
{factoid &&
story &&
idiom &&
joke &&
(view.includes('all') || view.includes('story')) &&
story.map((holidayValue, idx) => (
<Box
border={'1px solid #E2E2E2'}
borderRadius={'10px'}
padding={'20px'}
my={'20px'}
boxShadow="lg"
key={idx}
>
<Image
width={40}
height={40}
className="stories-icon"
src={storyImg}
alt="story"
/>
<Text>{holidayValue}</Text>
</Box>
))}
// the same 3 more times for the joke, idiom, factoid
)
目标是能够同时应用多个过滤器。如果未应用任何过滤器,则选择“全部”。如果未应用任何过滤器,按钮将返回原始状态。按钮样式是动态的并且取决于视图状态。问题是它的应用比应有的晚了一步。我认为这是因为
useState
的异步特性,但我不知道如何修复它。我正在使用 Chakra UI,但我认为它不会造成任何问题。示例位于下面的 GIF 中:
我建议根本不要在过滤器数组中添加
'all'
过滤器。 'all'
过滤器(意思是“所有项目”,又名“无过滤器”)是从!activeFilters.length
派生的派生状态。
这里的东西似乎按预期工作(codesandbox 链接):
import * as React from 'react'
export const FilterButtons: React.FC<object> = () => {
const filters = ['one', 'two', 'three', 'four']
const [activeFilters, setActiveFilters] = React.useState<string[]>([])
const toggleFilter = (filter?: string) =>
setActiveFilters((prevState) =>
filter
? prevState.includes(filter)
? prevState.filter((f) => f !== filter)
: filters.length - prevState.length === 1
? []
: [...prevState, filter]
: []
)
return (
<>
{filters.map((filter) => (
<button
key={filter}
onClick={() => toggleFilter(filter)}
className={activeFilters.includes(filter) ? 'active' : ''}
>
{filter}
</button>
))}
<button
className={activeFilters.length ? '' : 'active'}
onClick={() => toggleFilter()}
disabled={!activeFilters.length}
>
all
</button>
</>
)
}
如果
toggleFilter
内的嵌套三元数难以阅读,这里有一个更明确的版本:
function toggleFilter(filter?: string) {
return setActiveFilters((prevState) => {
if (filter) {
if (prevState.includes(filter)) {
// remove filter
return prevState.filter((f) => f !== filter)
} else if (filters.length - prevState.length !== 1) {
// add filter
return [...prevState, filter]
}
}
// remove all filters
return []
})
}