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

ReactJS数组元素跨数组转移失效及重复key问题排查

问题:电影分类Chip组件功能失效+重复Key错误

我有两个存储电影分类的数组selectedGenres和genres,想用Material UI的<Chip />组件通过.map渲染分类。预期效果是:点击未选中的分类(来自genres),就把它移到selectedGenres并从genres里移除;点击已选中的分类则反向操作。但现在onClick和onDelete完全没效果,还弹出「两个不同对象使用相同key」的错误。数据是从TMDB API获取的,相关代码如下:

Genres.jsx代码

import Chip from "@mui/material/Chip";

const Genres = ({
  selectedGenres,
  setSelectedGenres,
  genres,
  setGenres,
  type,
  setPage,
}) => {

  // 添加与移除函数

  const handleAdd = (genre) => {
    setSelectedGenres([...selectedGenres, genre]);
    setGenres(genres.filter((g) => g.id !== genre.id));
  };

  const handleRemove = (genre) => {
    setGenres([...genres, genre]);
    setSelectedGenres(
      selectedGenres.filter((selected) => selected.id !== genre.id)
    );
  };

  const fetchGenres = async () => {
    const apiKey = MY_API_KEY;

    const { data } = await axios.get(
      `https://api.themoviedb.org/3/genre/${type}/list?api_key=${apiKey}&language=en-US`
    );
    setGenres(data.genres);
  };
  useEffect(() => {
    fetchGenres();
  });

  return (
  
    // 两类Chip的React渲染函数

    {selectedGenres.map((genre) => (
      <Chip
        sx={{ margin: "5px" }}
        label={genre.name}
        key={genre.id}
        color="primary"
        clickable
        onDelete={() => handleRemove(genre)}
      />
    ))}

    {genres.map((genre) => (
      <Chip
        sx={{ margin: "5px" }}
        label={genre.name}
        key={genre.id}
        clickable
        onClick={() => handleAdd(genre)}
      />
    ))}
  )
}

Movies.jsx代码

import Genres from "./Genres.jsx";

const Movies = () => {
  const [genres, setGenres] = useState([]);
  const [selectedGenres, setSelectedGenres] = useState([]);

  return (
    <Genres
      type="movie"
      selectedGenres={selectedGenres}
      setSelectedGenres={setSelectedGenres}
      genres={genres}
      setGenres={setGenres}
      setPage={setPage}
    />
  )
}

解答

问题根源

  1. 重复Key错误原因:
    你的useEffect没有设置依赖项,组件每次渲染都会重新调用fetchGenres,把TMDB返回的全部分类重新塞回genres数组。这就导致已经被移到selectedGenres的分类,又被重新加回genres,同一个分类ID同时出现在两个数组里,渲染时触发重复key警告。

  2. 功能失效原因:
    同样是useEffect无依赖的问题——每次你点击Chip修改数组状态,组件都会重渲染,进而触发fetchGenres重置genres数组,把你刚修改的状态直接覆盖,所以点击操作看起来完全没效果。

修复方案

1. 给useEffect添加正确依赖项

只在组件首次挂载或type参数变化时拉取分类,避免重复请求重置数据:

import { useEffect } from 'react'; // 别忘了导入useEffect

// ...其他代码

useEffect(() => {
  fetchGenres();
}, [type]); // 仅当type变化时重新执行

2. 优化状态更新逻辑(可选)

把状态更新改成使用回调函数,避免闭包导致的状态快照问题:

const handleAdd = (genre) => {
  setSelectedGenres(prevState => [...prevState, genre]);
  setGenres(prevState => prevState.filter(g => g.id !== genre.id));
};

const handleRemove = (genre) => {
  setGenres(prevState => [...prevState, genre]);
  setSelectedGenres(prevState => prevState.filter(selected => selected.id !== genre.id));
};

3. 补充缺失的导入

你的代码里用了axios但没导入,需要补上:

import axios from 'axios';

总结

重复key错误和功能失效不是key的用法问题(用genre.id作为key是正确的),核心原因是useEffect没有依赖项导致的重复请求重置数据。修复useEffect的依赖后,两个问题都会解决。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 03:21:02