【问题标题】:Getting an error message when I filter the redux state过滤 redux 状态时收到错误消息
【发布时间】:2021-09-25 00:45:08
【问题描述】:

当我尝试过滤从 API 提取的数据结果时遇到错误。

当我使用我的 searchBar 组件在我输入任何内容时立即过滤 redux 数据时出现错误消息。

以下是错误信息:

“错误:[Immer] 一个 immer 生产者返回了一个新值修改了它的草稿。要么返回一个新值修改草稿。”

如何过滤数据并返回新数据?

以下是我正在使用的组件和 Redux TK slice。

Home.js 组件

import React, {useState, useEffect} from 'react';
import { Col, Container, Row } from 'react-bootstrap';
import { useDispatch, useSelector } from 'react-redux';
import { getCountries } from '../redux/search';
// import LazyLoading from 'react-list-lazy-load';
// import { updateText } from '../redux/searchTerm';


import SearchBar from '../components/SearchBar';
import CardComponent from '../components/Card';
import DropdownMenu from '../components/DropdownMenu';


const Home = () => {
    const dispatch = useDispatch();
    // const [ countries, setCountries ] = useState(null);
    const countries = useSelector((state) => state.search);
    

    // const filterCountry = (searchCountry) => {
    //     countries.list.payload.filter(country => country.name.toLowerCase() == searchCountry.toLowerCase())
    // }

    useEffect(() => {
        dispatch(getCountries());
        console.log(countries);
    }, [dispatch]);

    

    // console.log(countries.filter(country => country.region.toLowerCase() === 'africa'))

    return (
        <>
            <Container className="home-container">
                <Row>
                    <Col sm={12} md={6} className="left">
                        <SearchBar />
                    </Col>
                    <Col sm={12} md={6} className="right">
                        <DropdownMenu/>
                    </Col>
                </Row>
                <Row className="countryRow">
                    { countries.status == 'success' 
                    ?<>
                        {countries.list.map((country) => {
                            return <CardComponent key={country.name} 
                                        title={country.name} 
                                        image={country.flags[0]}
                                        population={country.population} 
                                        region={country.region} 
                                        capital={country.capital}/>
                        })}
                    </> 
                    :<div>Loading.....</div>
                    }
                </Row>
            </Container>
        </>
    )
}

export default Home;

SearchBar.js

import React from 'react';
import {useSelector, useDispatch} from 'react-redux';
// import { updateText } from '../redux/searchTerm';
import { searchTerm } from '../redux/search';


const SearchBar = () => {
    const query = useSelector((state) => state.searchDefault);
    const dispatch = useDispatch();

    return (
        <>
            <form>
                <input 
                    className="search" 
                    type="search" 
                    placeholder="Search for a country" 
                    value={query}
                    onChange={(e) => dispatch(searchTerm(e.target.value))}/>
            </form>
        </>
    )
}

export default SearchBar;

search.js 切片

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 await resp.data;
        } catch (error) {
            console.log(error.message);
        }
    }
)

const searchSlice = createSlice({
    name: 'searchDefault',
    initialState: {
        list: [],
        status: null,
        value: ''
    },
    reducers: {
        searchTerm: (state, action) => {
            // console.log(state.value);
            state.value = action.payload
            // console.log(state.value);
            state.list.filter( country => country.name == state.value);
            return state.list;
            // console.log(state.list);
      
        }
    },
    extraReducers: {
        [getCountries.pending]: (state, action) => {
            state.status = 'loading'
        },
        [getCountries.fulfilled]: (state, payload) => {
            console.log(payload)
            state.list = payload.payload
            state.status = 'success'
        },
        [getCountries.rejected]: (state, action) => {
            state.status = 'failed'
        }
    }
})

export const { searchTerm } = searchSlice.actions;
export default searchSlice.reducer;

【问题讨论】:

  • 猜猜如何把它变成一个像const filteredState = state.list.filter( country =&gt; country.name == state.value); return filteredState这样的新变量

标签: javascript reactjs react-redux redux-toolkit


【解决方案1】:

根据 redux-toolkit 中的docs

Redux Toolkit 的 createReducer API 在内部自动使用 Immer。因此,在传递给 createReducer 的任何 case reducer 函数中“改变”状态已经是安全的:

和 Immer docs:

也允许从生产者函数中任意返回其他数据。 但前提是您没有修改草稿

在你的 reducer 中,你正在使用 immer API 来改变值 (state.value = action.payload) 并返回结果。然而,Immer 只允许你做两件事之一,不能同时做。所以要么你改变状态:

searchTerm: (state, action) => {  
  state.value = action.payload; // notify redux that only the value property is dirty
}

或者完全替换新状态:

searchTerm: (state, action) => {  
  return { ...state, value: action.payload }; // replace the whole state
  // useful when you need to reset all state to the default value
}

大多数时候,你只需要告诉 redux 切片的特定属性发生了变化,然后 redux 将只通知订阅了该属性的组件(通过useSelector)重新渲染。所以删除reducer中的return语句,你的代码应该可以再次工作:

searchTerm: (state, action) => {  
  state.value = action.payload;
  state.list = state.list.filter( country => country.name == state.value);
  // remove the return statement
}

【讨论】:

  • 改变状态 +1 我学到了一些新东西。谢谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-02-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多