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

RTK Query请求后Redux Toolkit缓存重复数据,如何消除重复Key警告?

解决React Native自动补全功能的重复Key警告问题

问题背景

基于React Native、Expo和Redux Toolkit开发项目,实现输入时的自动补全功能:用户每输入一个字符,通过RTK Query向本地数据库发起查询。负责该功能的AddressSearch组件接收用户输入、渲染内容,通过防抖后的输入查询数据库,并将结果用FlatList展示。

组件代码

const AddressSearch = () => {
  const styles = stylesFn();
  // 输入框实时内容
  const [input, setInput] = useState("");
  // 防抖后传给API的内容
  const [debouncedInput, setDebouncedInput] = useState("");
  // RTKQ 跳过请求的开关,避免过早请求
  const [skip, setSkip] = useState(true);

  let {
    data: rawSuggestions,
    isFetching,
    isLoading,
    isSuccess,
    isError,
    error,
  } = useGetCarQuery(debouncedInput, { skip });

  const updateSearchedAddress = (text) => {
    text.length > 2 ? setSkip(false) : setSkip(true);
    // 更新输入框状态
    setInput(text);
    // 2秒后更新传给API的内容
    setTimeout(() => {
      setDebouncedInput(text);
    }, 2000);
  };

  return (
    <View>
      {/* ... 其他UI代码 */}
      <FlatList
        scrollEnabled={true}
        showVerticalScrollIndicator={true}
        data={rawSuggestions ? rawSuggestions.results : null}
        renderItem={({ item, index, separators }) => (
          <Pressable key={item.id}>
            <Text>
              {item.id} {item.make} {item.model} {item.version} {item.fuel}
            </Text>
          </Pressable>
        )}
      />
      {/* ... 其他UI代码 */}
    </View>
  );
};

export default AddressSearch;

出现的警告

Warning: Encountered two children with the same key, `246`. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.

问题分析

快速输入时多次触发请求,加上RTK Query默认无数据去重机制,导致旧请求的结果未被取消,新旧数据同时存在于界面中,出现重复key。现有防抖逻辑未处理旧定时器,导致多次触发API请求。

解决方案

1. 优化防抖逻辑,取消旧定时器

原防抖逻辑每次输入都会创建新定时器,旧定时器未被清除,导致多次更新debouncedInput。修改后确保同一时间只有一个定时器生效:

const AddressSearch = () => {
  // ... 原有状态
  const [debounceTimeoutId, setDebounceTimeoutId] = useState(null); // 新增定时器ID状态

  const updateSearchedAddress = (text) => {
    text.length > 2 ? setSkip(false) : setSkip(true);
    setInput(text);
    
    // 清除之前的定时器,避免旧请求触发
    if (debounceTimeoutId) {
      clearTimeout(debounceTimeoutId);
    }
    
    // 设置新定时器
    const timeoutId = setTimeout(() => {
      setDebouncedInput(text);
    }, 2000);
    setDebounceTimeoutId(timeoutId);
  };

  // 组件卸载时清除定时器,避免内存泄漏
  useEffect(() => {
    return () => {
      if (debounceTimeoutId) {
        clearTimeout(debounceTimeoutId);
      }
    };
  }, [debounceTimeoutId]);

  // ... 其他原有代码
};

2. 配置RTK Query取消旧请求

在useGetCarQuery中添加cancelPreviousQueries: true选项,确保新请求发起时自动取消未完成的旧请求,避免旧数据干扰:

let {
  data: rawSuggestions,
  isFetching,
  isLoading,
  isSuccess,
  isError,
  error,
} = useGetCarQuery(debouncedInput, { 
  skip,
  cancelPreviousQueries: true // 新增该选项
});

3. 移除冗余本地状态,直接使用RTK Query返回数据

原代码中用useState和useEffect同步rawSuggestions到suggestions,容易出现状态不同步。直接使用RTK Query返回的rawSuggestions:

// 移除以下两行冗余代码:
// const [suggestions, setSuggestions] = useState();
// useEffect(() => { if (rawSuggestions) setSuggestions(rawSuggestions); }, [rawSuggestions]);

// 修改FlatList的data属性:
data={rawSuggestions?.results || []}

4. 兜底方案:确保key唯一(若后端返回重复ID)

如果后端确实存在返回重复ID的情况,可结合索引生成唯一key(优先解决后端数据问题,此为兜底):

<Pressable key={`${item.id}-${index}`}>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 21:50:36