You need to enable JavaScript to run this app. React基于自定义Hook实现Select时默认值失效、选项去重异常问题求解
优惠活动
大模型
产品
解决方案
定价
更多

React基于自定义Hook实现Select时默认值失效、选项去重异常问题求解

问题根因定位
  • 自定义Hook状态污染:当前实现把「接口拉取的原始全量餐厅数据」和「筛选后用于页面渲染的列表」存在同一个状态中,当用户选中非All的菜系时,筛选结果会直接覆盖原始全量数据,后续切回All选项时,没有完整的源数据可以用来加载全量列表,导致状态更新失效。同时All分支的筛选逻辑存在设计错误:用"all"字符串去匹配餐厅的菜系字段,本身没有匹配合理性,自然无法返回正确结果。
  • 下拉选项重复问题:之前尝试的去重逻辑[...new Set(restaurant.cuisine)]是对单个餐厅的菜系字符串做字符级去重,并没有对所有餐厅的菜系值做整体去重;同时旧代码的下拉选项是从筛选后的渲染列表中生成的,一旦做了筛选,其他菜系选项就会消失,加上All选项的value是动态绑定当前选中的cuisine值,没有固定为"All",进一步导致受控select的选中状态异常。
分步修复方案

1. 重构自定义Hook的状态逻辑

将原始全量数据和筛选后的展示列表拆分为独立状态,避免互相覆盖:

// Hooks/useRestaurants.js
import { useState, useEffect } from "react";

const useRestaurants = (cuisine) => {
  // 独立状态存原始接口返回的全量数据,不会被筛选逻辑覆盖
  const [rawRestaurants, setRawRestaurants] = useState([]);
  // 独立状态存筛选后用于页面渲染的列表
  const [displayRestaurants, setDisplayRestaurants] = useState([]);

  // 第一个Effect:只负责拉取接口原始数据,和筛选逻辑完全解耦
  useEffect(() => {
    fetch("https://redi-final-restaurants.herokuapp.com/restaurants")
      .then((res) => res.json())
      .then((result) => {
        setRawRestaurants(result.results);
        // 初始状态为All,直接把全量数据作为默认展示列表
        setDisplayRestaurants(result.results);
      })
      .catch((e) => console.log("接口请求错误", e));
  }, []);

  // 第二个Effect:只负责根据选中的cuisine值做筛选
  useEffect(() => {
    // 接口未返回数据时直接跳过,避免空值报错
    if (!rawRestaurants.length) return;

    if (cuisine === "All") {
      // 选中All时直接展示全量原始数据,不需要做includes匹配
      setDisplayRestaurants([...rawRestaurants]);
    } else {
      const filtered = rawRestaurants.filter((restaurant) => 
        restaurant.cuisine.toLowerCase().includes(cuisine.toLowerCase())
      );
      setDisplayRestaurants(filtered);
    }
  }, [cuisine, rawRestaurants]);

  // 返回两个值:当前要展示的列表、原始全量数据(供下拉选项生成使用)
  return [displayRestaurants, rawRestaurants];
};

export default useRestaurants;

2. 修复App组件的传参逻辑

把Hook返回的原始全量数据传给Form组件,用来生成稳定的下拉选项,避免从筛选后的渲染列表生成选项:

// App.js
import React, { useState } from "react";
import useRestaurants from "./useRestaurants";
import Form from "./Form";
import Restaurant from "./Restaurant";
import "./styles.css";

export default function App() {
  const [cuisine, setCuisine] = useState("All");
  // 解构两个返回值:displayRestaurants是列表渲染用的,rawRestaurants是下拉选项生成用的
  const [displayRestaurants, rawRestaurants] = useRestaurants(cuisine);

  const onChangeHandler = (e) => {
    setCuisine(e.target.value);
  };

  return (
    <div className="App">
      <Form
        onChangeHandler={onChangeHandler}
        rawRestaurants={rawRestaurants}
        cuisine={cuisine}
      />
      {displayRestaurants.map((restaurant) => (
        <Restaurant restaurant={restaurant} key={restaurant.id} />
      ))}
    </div>
  );
}

3. 修复Form组件的下拉选项逻辑

  • 固定All选项的value为"All",不要动态绑定选中值
  • 从传入的原始全量数据中,用Set自动去重所有菜系值,再渲染为option
  • 给每个option绑定对应的value值,保证受控select的选中态正常匹配
// Form.js
import React from "react";

const Form = ({ rawRestaurants, cuisine, onChangeHandler }) => {
  // 从原始全量数据中提取不重复的菜系值
  const uniqueCuisines = [...new Set(
    rawRestaurants.map(restaurant => restaurant.cuisine)
  )];

  return (
    <select onChange={onChangeHandler} value={cuisine}>
      {/* 固定All选项的value为"All",不要动态绑定当前选中值 */}
      <option value="All">All</option>
      {uniqueCuisines.map((cuisineItem) => (
        <option key={cuisineItem} value={cuisineItem}>
          {cuisineItem}
        </option>
      ))}
    </select>
  );
};

export default Form;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.31 05:54:07