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

使用Formik和FieldArray实现下拉选项联动过滤的技术咨询

解决方案

要实现选中选项后其他下拉移除该选项、新增下拉过滤已选的需求,只需通过过滤每个下拉的可选列表来实现,具体修改如下:

1. 新增可用选项过滤函数

在App组件内定义一个函数,根据当前下拉的索引,排除其他已选中的选项,返回当前下拉可选择的影片列表:

const getAvailableFilms = (currentIndex) => {
  // 收集除当前下拉外,其他所有已选中的影片(排除未选择的空项)
  const selectedFilms = formik.values.filmsArray
    .filter((_, idx) => idx !== currentIndex)
    .map(item => item.filmName)
    .filter(item => Object.keys(item).length > 0);

  // 从全部影片中过滤掉已被选中的选项
  return top100Films.filter(film => 
    !selectedFilms.some(selected => selected.label === film.label)
  );
};

2. 修改Autocomplete的选项来源

将每个Autocomplete的options属性替换为上述函数的返回值,同时调整onChange逻辑,确保取消选择时保持数据结构一致:

<Autocomplete
  name={`filmsArray[${index}].filmName`}
  disablePortal
  id={`combo-box-demo-${index}`} // 每个下拉设置唯一ID
  options={getAvailableFilms(index)} // 传入当前索引获取过滤后的选项
  fullWidth
  renderInput={(params) => (
    <TextField {...params} label="select film" />
  )}
  onChange={(event, value) => {
    // 取消选择时设置为空对象,保持数组项结构统一
    formik.setFieldValue(
      `filmsArray[${index}].filmName`,
      value || {}
    );
  }}
  // 设置当前选中值,确保回显正确
  value={formik.values.filmsArray[index].filmName}
/>

3. 完整修改后的代码

import * as React from "react";
import TextField from "@mui/material/TextField";
import Autocomplete from "@mui/material/Autocomplete";
import { useFormik, FormikProvider, FieldArray } from "formik";

import Button from "@material-ui/core/Button";

export default function App() {
  const filmGroup = { filmName: {} };

  const formik = useFormik({
    initialValues: {
      filmsArray: [filmGroup]
    },
    onSubmit: (values) => {
      alert(JSON.stringify(values, null, 2));
    }
  });

  // 过滤当前下拉可用的影片选项
  const getAvailableFilms = (currentIndex) => {
    const selectedFilms = formik.values.filmsArray
      .filter((_, idx) => idx !== currentIndex)
      .map(item => item.filmName)
      .filter(item => Object.keys(item).length > 0);

    return top100Films.filter(film => 
      !selectedFilms.some(selected => selected.label === film.label)
    );
  };

  return (
    <FormikProvider value={formik}>
      <form onSubmit={formik.handleSubmit}>
        <FieldArray
          name="filmsArray"
          render={(arrayfilms) => (
            <div>
              {formik.values.filmsArray.map((friend, index) => (
                <div key={index} style={{ display: "flex", gap: "8px", marginBottom: "8px" }}>
                  <Autocomplete
                    name={`filmsArray[${index}].filmName`}
                    disablePortal
                    id={`combo-box-demo-${index}`}
                    options={getAvailableFilms(index)}
                    fullWidth
                    renderInput={(params) => (
                      <TextField {...params} label="select film" />
                    )}
                    onChange={(event, value) => {
                      formik.setFieldValue(
                        `filmsArray[${index}].filmName`,
                        value || {}
                      );
                    }}
                    value={formik.values.filmsArray[index].filmName}
                  />

                  <button
                    type="button"
                    onClick={() => arrayfilms.remove(index)}
                    style={{ alignSelf: "center" }}
                  >
                    -
                  </button>
                </div>
              ))}
              <button
                type="button"
                onClick={() => arrayfilms.push({ filmName: {} })}
                style={{ marginBottom: "16px" }}
              >
                +
              </button>
            </div>
          )}
        />
        <Button color="primary" variant="contained" fullWidth type="submit">
          Submit
        </Button>
      </form>
    </FormikProvider>
  );
}

// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top
const top100Films = [
  { label: "The Shawshank Redemption", year: 1994 },
  { label: "The Godfather", year: 1972 },
  { label: "The Godfather: Part II", year: 1974 },
  { label: "The Dark Knight", year: 2008 },
  { label: "12 Angry Men", year: 1957 },
  { label: "Schindler's List", year: 1993 },
  { label: "Pulp Fiction", year: 1994 },
  {
    label: "The Lord of the Rings: The Return of the King",
    year: 2003
  },
  { label: "The Good, the Bad and the Ugly", year: 1966 },
  { label: "Fight Club", year: 1999 },
  {
    label: "The Lord of the Rings: The Fellowship of the Ring",
    year: 2001
  },
  {
    label: "Star Wars: Episode V - The Empire Strikes Back",
    year: 1980
  },
  { label: "Forrest Gump", year: 1994 },
  { label: "Inception", year: 2010 },
  {
    label: "The Lord of the Rings: The Two Towers",
    year: 2002
  },
  { label: "One Flew Over the Cuckoo's Nest", year: 1975 },
  { label: "Goodfellas", year: 1990 },
  { label: "The Matrix", year: 1999 },
  { label: "Seven Samurai", year: 1954 },
  {
    label: "Star Wars: Episode IV - A New Hope",
    year: 1977
  },
  { label: "City of God", year: 2002 },
  { label: "Se7en", year: 1995 },
  { label: "The Silence of the Lambs", year: 1991 },
  { label: "It's a Wonderful Life", year: 1946 },
  { label: "Life Is Beautiful", year: 1997 },
  { label: "The Usual Suspects", year: 1995 },
  { label: "Léon: The Professional", year: 1994 },
  { label: "Spirited Away", year: 2001 },
  { label: "Saving Private Ryan", year: 1998 },
  { label: "Once Upon a Time in the West", year: 1968 },
  { label: "American History X", year: 1998 },
  { label: "Interstellar", year: 2014 },
  { label: "Casablanca", year: 1942 },
  { label: "City Lights", year: 1931 },
  { label: "Psycho", year: 1960 },
  { label: "The Green Mile", year: 1999 },
  { label: "The Intouchables", year: 2011 },
  { label: "Modern Times", year: 1936 },
  { label: "Raiders of the Lost Ark", year: 1981 },
  { label: "Rear Window", year: 1954 },
  { label: "The Pianist", year: 2002 },
  { label: "The Departed", year: 2006 },
  { label: "Terminator 2: Judgment Day", year: 1991 },
  { label: "Back to the Future", year: 1985 },
  { label: "Whiplash", year: 2014 },
  { label: "Gladiator", year: 2000 },
  { label: "Memento", year: 2000 },
  { label: "The Prestige", year: 2006 },
  { label: "The Lion King", year: 1994 },
  { label: "Apocalypse Now", year: 1979 },
  { label: "Alien", year: 1979 },
  { label: "Sunset Boulevard", year: 1950 },
  {
    label:
      "Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb",
    year: 1964
  },
  { label: "The Great Dictator", year: 1940 },
  { label: "Cinema Paradiso", year: 1988 },
  { label: "The Lives of Others", year: 2006 },
  { label: "Grave of the Fireflies", year: 1988 },
  { label: "Paths of Glory", year: 1957 },
  { label: "Django Unchained", year: 2012 },
  { label: "The Shining", year: 1980 },
  { label: "WALL·E", year: 2008 },
  { label: "American Beauty", year: 1999 },
  { label: "The Dark Knight Rises", year: 2012 },
  { label: "Princess Mononoke", year: 1997 },
  { label: "Aliens", year: 1986 },
  { label: "Oldboy", year: 2003 },
  { label: "Once Upon a Time in America", year: 1984 },
  { label: "Witness for the Prosecution", year: 1957 },
  { label: "Das Boot", year: 1981 },
  { label: "Citizen Kane", year: 1941 },
  { label: "North by Northwest", year: 1959 },
  { label: "Vertigo", year: 1958 },
  {
    label: "Star Wars: Episode VI - Return of the Jedi",
    year: 1983
  },
  { label: "Reservoir Dogs", year: 1992 },
  { label: "Braveheart", year: 1995 },
  { label: "M", year: 1931 },
  { label: "Requiem for a Dream", year: 2000 },
  { label: "Amélie", year: 2001 },
  { label: "A Clockwork Orange", year: 1971 },
  { label: "Like Stars on Earth", year: 2007 },
  { label: "Taxi Driver", year: 1976 },
  { label: "Lawrence of Arabia", year: 1962 },
  { label: "Double Indemnity", year: 1944 },
  {
    label: "Eternal Sunshine of the Spotless Mind",
    year: 2004
  },
  { label: "Amadeus", year: 1984 },
  { label: "To Kill a Mockingbird", year: 1962 },
  { label: "Toy Story 3", year: 2010 },
  { label: "Logan", year: 2017 },
  { label: "Full Metal Jacket", year: 1987 },
  { label: "Dangal", year: 2016 },
  { label: "The Sting", year: 1973 },
  { label: "2001: A Space Odyssey", year: 1968 },
  { label: "Singin' in the Rain", year: 1952 },
  { label: "Toy Story", year: 1995 },
  { label: "Bicycle Thieves", year: 1948 },
  { label: "The Kid", year: 1921 },
  { label: "Inglourious Basterds", year: 2009 },
  { label: "Snatch", year: 2000 },
  { label: "3 Idiots", year: 2009 },
  { label: "Monty Python and the Holy Grail", year: 1975 }
];

关键逻辑说明

  • 过滤函数getAvailableFilms:遍历表单数组,收集除当前下拉外的所有已选中影片,再从全部影片列表中排除这些已选项,得到当前下拉的可选列表。
  • Autocomplete的value属性:显式设置当前选中值,确保表单值变化时下拉能正确回显。
  • 取消选择处理:在onChange中把null转为空对象,保持数组中每个项的结构一致,避免过滤逻辑出错。

这样修改后,任意下拉选中选项后,其他下拉会自动移除该选项;新增的下拉也会自动过滤掉已被选中的选项,完全符合需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 08:45:30