You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何优化useEffect中多if-else的筛选逻辑,让代码更简洁?

宝可梦列表筛选逻辑重构优化

我在React的useEffect钩子中实现了一个筛选函数,依据三个下拉框的状态过滤显示的宝可梦内容。目前代码可正常运行,但结构复杂,新人难以理解,我希望重构得更简洁高效。我考虑过使用switch语句,也想过用pokedex.id < x && pokedex.id > y替代当前的setOffset和setLimit逻辑。

现有筛选代码

useEffect(() => {
    setFilteredPokedex(
      pokedex
        .filter((pokedex) => {
          return (
            type === `all` ||
            pokedex.types.map((pt) => pt.type.name).includes(type)
          );
        })
        .filter((pokedex) => {
          if (form === `default` && generation === `all`) {
            setOffset(0);
            setLimit(905);
            return pokedex;
          } else if (form === `regional - alola`) {
            setOffset(995);
            setLimit(30);
            return pokedex?.name?.includes(`alola`);
          } else if (form === `regional - galar`) {
            setOffset(1065);
            setLimit(25);
            return pokedex?.name?.includes(`galar`);
          } else if (form === `regional - hisui`) {
            setOffset(1133);
            setLimit(20);
            return (
              pokedex?.name?.includes(`hisui`) ||
              pokedex?.name?.includes(`origin`)
            );
          } else if (form === `mega`) {
            setOffset(937);
            setLimit(70);
            return (
              pokedex?.name?.includes(`-mega`) ||
              pokedex?.name?.includes(`primal`)
            );
          } else if (form === `gmax`) {
            setOffset(1099);
            setLimit(35);
            return pokedex?.name?.includes(`gmax`);
          } else if (generation === `gen1`) {
            setOffset(0);
            setLimit(151);
            return pokedex;
          } else if (generation === `gen2`) {
            setOffset(151);
            setLimit(100);
            return pokedex;
          } else if (generation === `gen3`) {
            setOffset(251);
            setLimit(135);
            return pokedex;
          } else if (generation === `gen4`) {
            setOffset(386);
            setLimit(107);
            return pokedex;
          } else if (generation === `gen5`) {
            setOffset(493);
            setLimit(156);
            return pokedex;
          } else if (generation === `gen6`) {
            setOffset(649);
            setLimit(72);
            return pokedex;
          } else if (generation === `gen7`) {
            setOffset(721);
            setLimit(88);
            return pokedex;
          } else if (generation === `gen8`) {
            setOffset(809);
            setLimit(96);
            return pokedex;
          }
        }),
    );
  }, [
    pokedex,
    form,
    generation,
    type
  ]);

补充的下拉框代码

<PokedexDropdown>
  <label htmlFor="form">Form</label>
  <select
    name="form"
    id="form"
    value={form}
    onChange={(e) => {
      setForm(e.target.value);
      setGeneration(`all`);
      setType(`all`);
    }}
  >
    <option value="default">Default</option>
    <option value="regional - alola">Regional - Alola</option>
    <option value="regional - galar">Regional - Galar</option>
    <option value="regional - hisui">Regional - Hisui</option>
    <option value="mega">Mega</option>
    <option value="gmax">Gmax</option>
  </select>
</PokedexDropdown>

<PokedexDropdown className={form === `default` ? `` : `hidden`}>
  <label htmlFor="generation">Generation</label>
  <select
    name="generation"
    id="generation"
    value={generation}
    onChange={(e) => {
      setGeneration(e.target.value);
      setForm(`default`);
      setType(`all`);
    }}
  >
    <option value="all">All</option>
    <option value="gen1">Generation I</option>
    <option value="gen2">Generation II</option>
    <option value="gen3">Generation III</option>
    <option value="gen4">Generation IV</option>
    <option value="gen5">Generation V</option>
    <option value="gen6">Generation VI</option>
    <option value="gen7">Generation VII</option>
    <option value="gen8">Generation VIII</option>
  </select>
</PokedexDropdown>

<PokedexDropdown>
  <label htmlFor="type">Type</label>
  <select
    name="type"
    id="type"
    value={type}
    onChange={(e) => {
      setType(e.target.value);
    }}
  >
    <option value="all">All</option>
    <option value="bug">Bug</option>
    <option value="dark">Dark</option>
    <option value="dragon">Dragon</option>
    <option value="electric">Electric</option>
    <option value="fairy">Fairy</option>
    <option value="fighting">Fighting</option>
    <option value="fire">Fire</option>
    <option value="flying">Flying</option>
    <option value="ghost">Ghost</option>
    <option value="grass">Grass</option>
    <option value="ground">Ground</option>
    <option value="ice">Ice</option>
    <option value="normal">Normal</option>
    <option value="poison">Poison</option>
    <option value="psychic">Psychic</option>
    <option value="rock">Rock</option>
    <option value="steel">Steel</option>
    <option value="water">Water</option>
  </select>
</PokedexDropdown>

优化方案

1. 抽离配置常量,分离数据与逻辑

把形态、世代的筛选规则和参数抽成独立配置对象,避免大量嵌套的if-else,后续新增规则只需修改配置:

// 形态筛选配置:key对应下拉框value,包含offset、limit和筛选函数
const FORM_CONFIG = {
  'default': { offset: 0, limit: 905, filter: () => true },
  'regional - alola': { offset: 995, limit: 30, filter: (poke) => poke.name.includes('alola') },
  'regional - galar': { offset: 1065, limit: 25, filter: (poke) => poke.name.includes('galar') },
  'regional - hisui': { offset: 1133, limit: 20, filter: (poke) => poke.name.includes('hisui') || poke.name.includes('origin') },
  'mega': { offset: 937, limit: 70, filter: (poke) => poke.name.includes('-mega') || poke.name.includes('primal') },
  'gmax': { offset: 1099, limit: 35, filter: (poke) => poke.name.includes('gmax') },
};

// 世代筛选配置:用宝可梦id范围替代offset/limit,更直观
const GENERATION_CONFIG = {
  'all': { minId: 1, maxId: 905 },
  'gen1': { minId: 1, maxId: 151 },
  'gen2': { minId: 152, maxId: 251 },
  'gen3': { minId: 252, maxId: 386 },
  'gen4': { minId: 387, maxId: 493 },
  'gen5': { minId: 494, maxId: 649 },
  'gen6': { minId: 650, maxId: 721 },
  'gen7': { minId: 722, maxId: 809 },
  'gen8': { minId: 810, maxId: 905 },
};

2. 重构useEffect,拆分筛选逻辑

将类型、形态、世代的筛选拆分为独立函数,避免在filter回调中修改状态,减少不必要的组件渲染:

useEffect(() => {
  let currentOffset = 0;
  let currentLimit = 905;
  let formFilter = () => true;
  let generationFilter = () => true;

  // 处理形态筛选逻辑
  if (form !== 'default') {
    const { offset, limit, filter } = FORM_CONFIG[form];
    currentOffset = offset;
    currentLimit = limit;
    formFilter = filter;
  } else {
    // 处理世代筛选逻辑(默认形态下生效)
    const { minId, maxId } = GENERATION_CONFIG[generation];
    currentOffset = minId - 1; // 若offset为数组索引,对应id起始位置的索引
    currentLimit = maxId - minId + 1;
    generationFilter = (poke) => poke.id >= minId && poke.id <= maxId;
  }

  // 处理类型筛选逻辑
  const typeFilter = (poke) => {
    return type === 'all' || poke.types.some(t => t.type.name === type);
  };

  // 合并所有筛选条件,生成最终列表
  const filteredList = pokedex.filter(poke => 
    typeFilter(poke) && formFilter(poke) && generationFilter(poke)
  );

  // 统一设置状态,避免多次触发渲染
  setOffset(currentOffset);
  setLimit(currentLimit);
  setFilteredPokedex(filteredList);
}, [pokedex, form, generation, type]);

3. 下拉框代码优化(可选)

把下拉框选项也用配置生成,减少重复代码,后续新增选项只需修改配置:

// 形态选项配置
const FORM_OPTIONS = [
  { value: 'default', label: 'Default' },
  { value: 'regional - alola', label: 'Regional - Alola' },
  { value: 'regional - galar', label: 'Regional - Galar' },
  { value: 'regional - hisui', label: 'Regional - Hisui' },
  { value: 'mega', label: 'Mega' },
  { value: 'gmax', label: 'Gmax' },
];

// 世代选项配置
const GENERATION_OPTIONS = [
  { value: 'all', label: 'All' },
  { value: 'gen1', label: 'Generation I' },
  { value: 'gen2', label: 'Generation II' },
  { value: 'gen3', label: 'Generation III' },
  { value: 'gen4', label: 'Generation IV' },
  { value: 'gen5', label: 'Generation V' },
  { value: 'gen6', label: 'Generation VI' },
  { value: 'gen7', label: 'Generation VII' },
  { value: 'gen8', label: 'Generation VIII' },
];

渲染下拉框时直接映射配置:

<select
  name="form"
  id="form"
  value={form}
  onChange={(e) => {
    setForm(e.target.value);
    setGeneration('all');
    setType('all');
  }}
>
  {FORM_OPTIONS.map(opt => (
    <option key={opt.value} value={opt.value}>{opt.label}</option>
  ))}
</select>

优化亮点

  • 可读性提升:配置与逻辑分离,新人快速理解规则,无需通读冗长的if-else
  • 可维护性提升:新增/修改筛选规则只需调整配置对象,无需改动核心筛选逻辑
  • 性能优化:避免在filter回调中调用setState,减少不必要的组件重渲染
  • 逻辑直观:世代筛选用宝可梦官方id范围替代offset,更符合认知习惯

内容的提问来源于stack exchange,提问作者Tibo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 10:05:36