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

React+TS+Redux版Rick and Morty应用:实时筛选异常修复求助

解决方案:Rick and Morty 角色展示应用问题修复

1. Redux 状态结构拆分

先把状态拆分成全量角色池和筛选结果集,避免筛选操作污染原始数据,同时保留搜索关键词、加载状态和当前页码:

interface CharacterState {
  allCharacters: Character[]; // 仅在搜索为空+滚动时新增角色
  filteredCharacters: Character[]; // 实时筛选后的展示数据
  searchQuery: string;
  isLoading: boolean;
  currentPage: number;
}

// 初始状态
const initialState: CharacterState = {
  allCharacters: [],
  filteredCharacters: [],
  searchQuery: '',
  isLoading: false,
  currentPage: 1,
};

2. 修正无限滚动触发条件

只有当搜索框为空且未在加载中时,才触发下一页数据加载,避免筛选时误加载:

// 组件内的滚动监听逻辑
useEffect(() => {
  const handleScroll = () => {
    const { scrollTop, clientHeight, scrollHeight } = document.documentElement;
    // 触底阈值200px,仅搜索为空时加载
    if (scrollTop + clientHeight >= scrollHeight - 200 && !isLoading && searchQuery === '') {
      dispatch(fetchNextPageCharacters());
    }
  };
  window.addEventListener('scroll', handleScroll);
  return () => window.removeEventListener('scroll', handleScroll);
}, [isLoading, searchQuery, dispatch]);

3. Redux 异步加载与去重逻辑

在加载新角色后,先通过id去重(API返回的角色id唯一),再合并到全量数组;同时只有搜索为空时,同步更新筛选结果:

// 异步加载action
export const fetchNextPageCharacters = createAsyncThunk(
  'characters/fetchNextPage',
  async (_, { getState }) => {
    const state = getState() as RootState;
    const res = await fetch(`https://rickandmortyapi.com/api/character?page=${state.characters.currentPage + 1}`);
    const data = await res.json();
    return data.results;
  }
);

// Reducer处理逻辑
extraReducers: (builder) => {
  builder
    .addCase(fetchNextPageCharacters.pending, (state) => {
      state.isLoading = true;
    })
    .addCase(fetchNextPageCharacters.fulfilled, (state, action) => {
      // 基于id去重,避免重复添加
      const uniqueNewChars = action.payload.filter(
        newChar => !state.allCharacters.some(char => char.id === newChar.id)
      );
      state.allCharacters = [...state.allCharacters, ...uniqueNewChars];
      state.currentPage += 1;
      state.isLoading = false;
      // 搜索为空时,同步筛选结果为全量数据
      if (state.searchQuery === '') {
        state.filteredCharacters = state.allCharacters;
      }
    })
    .addCase(fetchNextPageCharacters.rejected, (state) => {
      state.isLoading = false;
    });
}

4. 实时筛选逻辑实现

添加搜索关键词变更的action,基于全量角色池实时生成筛选结果,清空搜索框时自动恢复全量数据:

// 设置搜索关键词的action
export const setSearchQuery = createAction<string>('characters/setSearchQuery');

// Reducer处理筛选
builder.addCase(setSearchQuery, (state, action) => {
  state.searchQuery = action.payload;
  // 根据关键词筛选全量角色
  if (action.payload.trim() === '') {
    state.filteredCharacters = state.allCharacters;
  } else {
    const query = action.payload.toLowerCase();
    state.filteredCharacters = state.allCharacters.filter(char => 
      char.name.toLowerCase().includes(query)
    );
  }
});

组件内绑定搜索框:

<input
  type="text"
  value={searchQuery}
  onChange={(e) => dispatch(setSearchQuery(e.target.value))}
  placeholder="搜索角色名称..."
/>

5. 列表Key优化

直接用角色的id作为列表key(API返回的唯一标识),比随机key更稳定:

{filteredCharacters.map(char => (
  <CharacterCard key={char.id} character={char} />
))}

如果有特殊场景需要额外随机值,可结合id生成:key={${char.id}-${Math.random()}},但优先使用纯id。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 11:17:44