useState 使动态样式应用较晚

问题描述 投票:0回答:1

我正在开发 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 中:

reactjs asynchronous next.js react-hooks
1个回答
0
投票

我建议根本不要在过滤器数组中添加

'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 []
  })
}
© www.soinside.com 2019 - 2024. All rights reserved.