Redux Toolkit过滤状态触发Immer返回新值并修改草稿报错如何解决
问题原因
Redux Toolkit 内部使用 Immer 处理不可变数据更新,要求 reducer 逻辑只能二选一:要么直接修改入参的 draft state 对象,要么返回一个全新的 state 值,不能同时执行两个操作。
你当前的searchTerm reducer 同时做了两件触发冲突的事:
- 执行
state.value = action.payload修改了 draft state - 最后执行
return state.list返回了新值
同时你还存在两个逻辑错误:
Array.filter()不会修改原数组,直接调用state.list.filter()不会生效,必须把返回的新数组赋值给state- 直接修改原始的
list会丢失全量国家数据,后续搜索只能基于上一次过滤的结果查询,无法回到全量数据
修复方案
1. 调整 search.js slice 代码
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'; import axios from 'axios'; export const getCountries = createAsyncThunk( 'searchDefault/getCountries', async () => { try { const resp = await axios.get('https://restcountries.com/v2/all'); return resp.data; } catch (error) { console.log(error.message); } } ) const searchSlice = createSlice({ name: 'searchDefault', initialState: { originalList: [], // 新增:存储全量原始国家数据,不做修改 list: [], // 存储当前渲染的过滤后数据 status: null, value: '' }, reducers: { searchTerm: (state, action) => { const keyword = action.payload.trim().toLowerCase(); state.value = action.payload; // 基于原始全量数据过滤,结果赋值给渲染用的list state.list = state.originalList.filter(country => country.name.toLowerCase().includes(keyword) ); // 不要写return,Immer会自动基于你修改的draft生成新state } }, extraReducers: { [getCountries.pending]: (state) => { state.status = 'loading' }, [getCountries.fulfilled]: (state, {payload}) => { state.originalList = payload; // 全量数据存到originalList state.list = payload; // 初始渲染用全量数据 state.status = 'success' }, [getCountries.rejected]: (state) => { state.status = 'failed' } } }) export const { searchTerm } = searchSlice.actions; export default searchSlice.reducer;
2. 修复 SearchBar.js 取值逻辑
你之前取query的时候拿了整个slice的state对象,需要精准取value字段:
import React from 'react'; import {useSelector, useDispatch} from 'react-redux'; import { searchTerm } from '../redux/search'; const SearchBar = () => { // 修正取值路径 const query = useSelector((state) => state.searchDefault.value); const dispatch = useDispatch(); return ( <form onSubmit={(e) => e.preventDefault()}> {/* 阻止表单默认提交刷新页面 */} <input className="search" type="search" placeholder="Search for a country" value={query} onChange={(e) => dispatch(searchTerm(e.target.value))}/> </form> ) } export default SearchBar;
3. 修正 Home.js 取值路径
如果你的store注册reducer时用的key和slice的name一致为searchDefault,需要调整取值路径:
// 其他代码保持不变 const countries = useSelector((state) => state.searchDefault);
内容的提问来源于stack exchange,提问作者Micah johnson
相关产品推荐
相关产品推荐

