Redux Toolkit:状态在 reducer 中显示为 Proxy / undefined
Posted
技术标签:
【中文标题】Redux Toolkit:状态在 reducer 中显示为 Proxy / undefined【英文标题】:Redux Toolkit: state showing as Proxy / undefined within reducer 【发布时间】:2021-02-11 14:34:24 【问题描述】:解决了,谢谢! - 对于任何感兴趣的人,我试图在减速器中访问 state.tasks.data 但是由于范围,我可以通过 state.data 访问它,因为我已经在任务切片中。
编辑:我的变异状态错误问题现已修复,这是由于直接对状态进行排序而没有从中创建新数组。这已在sort()
之前使用concat()
修复。
我的新问题是我的removeTask reducer
中的state
不再可访问。它现在返回未定义。如果我console.log(state)
那么它将返回一个Proxy
并带有:
[[Handler]]: null,
[[Target]]: null,
[[IsRevoked]]:true
编辑 2: 我发现代理是由于 immer
在幕后造成的,它对突变做了一些事情,以便以 state
未突变的方式使用它。我还没有解决state.tasks.data
返回undefined
的问题。
我正在使用 react-redux 和 redux-toolkit。我只是在学习 Redux,所以我很头疼,不知道。
我在他们的网站上关注了 redux-toolkit 基本教程中的一些信息,其中说你可以在 reducer 中改变状态,因为工具包在幕后做了一些事情来阻止它实际改变状态:
https://redux-toolkit.js.org/tutorials/basic-tutorial
在任何情况下,他们提供的反例都会改变状态。增量减速器返回 state += 1
- 这工作正常
现在我有自己的事情了,我将initial state
设置为我已拉入的对象数组。见下文:
tasksSlice.js
:
import createSlice from "@reduxjs/toolkit";
import data from "data/tasks_data.json";
export const tasksSlice = createSlice(
name: "tasks",
initialState:
data: data,
,
reducers:
removeTask: (state, action) =>
const id = action.payload;
const data = state.tasks;
data = data.filter((item) => id !== item.id);
,
,
);
export const removeTask = tasksSlice.actions;
export const selectTasks = (state) => state.tasks.data;
export default tasksSlice.reducer;
现在我的tasks
组件中列出了任务。每个任务都使用taskItem
组件列出。在taskItem
组件内,我有一个删除按钮,我已将onClick
事件设置为使用removeTask
减速器。
<button
className=`$styles.task_button $styles.delete`
onClick=() => dispatch(removeTask(id))
>
<MdDeleteForever />
</button>
这是从data
传递“customer_id”字段,该字段从initial state
映射到任务列表中。
我希望能够删除任务,所以我试图通过过滤state.tasks.data
并返回除id
匹配的任务之外的所有内容来改变它(正如工具包所说的那样)。
很遗憾,我不断收到错误消息:
Error: Invariant failed: A state mutation was detected between dispatches, in the path 'tasks.data.0'. This may cause incorrect behavior.
tasks.js
- 任务容器组件(相当混乱):
import React, useState, useEffect from "react";
import SectionHeading from "components/SectionHeading/SectionHeading";
import useSelector from "react-redux";
import selectTasks from "redux/tasks/tasksSlice";
import TaskSelect from "./TaskSelect";
import TaskTabs from "./TaskTabs";
import TaskItem from "./TaskItem";
import styles from "./Tasks.module.scss";
// import tasks_data from "data/tasks_data.json";
const Tasks = () =>
const tasksData = useSelector(selectTasks);
console.log(tasksData);
const [taskTab, setTaskTab] = useState( activeTask: "All" );
const [size, setSize] = useState( width: 65, left: 0 );
// const [tasksData, setTasksData] = useState(tasks_data);
const taskTypes = [
"All",
"Quotes",
"Domains",
"SSL Setup",
"SEO Setup",
"Other",
];
useEffect(() =>
const activeBar = document.querySelector(".active_bar");
const active = document.querySelector(".active_btn");
if (size)
activeBar.style.width = `$active.offsetWidthpx`;
activeBar.style.transform = `translate($active.offsetLeftpx, $active.offsetToppx)`;
);
const setActive = (e, type) =>
setTaskTab( activeTask: type );
setSize(false);
const activeBar = document.querySelector(".active_bar");
activeBar.style.width = `$e.target.offsetWidthpx`;
activeBar.style.transform = `translate($e.target.offsetLeftpx, $e.target.offsetToppx)`;
;
const changeActive = (e) =>
setTaskTab( activeTask: e.target.value );
;
const getDaysDue = (days) =>
const days_due, overdue = days;
if (overdue === true)
if (days_due === -1)
return `$Math.abs(days_due) day overdue`;
else
return `$Math.abs(days_due) days overdue`;
else if (days_due === 0)
return "Today";
else if (days_due === 1)
return `$days_due day`;
else if (days_due > 1)
return `$days_due days`;
else
return "Error getting days due";
;
return (
<article className="tasks">
<TaskTabs
taskTypes=taskTypes
click=setActive
activeTask=taskTab.activeTask
data=tasksData
/>
<TaskSelect taskTypes=taskTypes change=changeActive />
<SectionHeading>Tasks: taskTab.activeTask</SectionHeading>
<section className=styles.tasks_list>
tasksData
.sort((a, b) => a.days.days_due - b.days.days_due)
.filter((task) =>
taskTab.activeTask === "All"
? true
: task.type === taskTab.activeTask
)
.map(
(
customer_id,
account_name,
days,
days: days_due, overdue ,
type,
) =>
return (
<TaskItem
key=customer_id
id=customer_id
name=account_name
days=getDaysDue(days)
overdue=overdue
daysDue=days_due
type=type
/>
);
)
</section>
</article>
);
;
export default Tasks;
TaskItem.js
:
import React from "react";
import useDispatch from "react-redux";
import removeTask from "redux/tasks/tasksSlice";
import Link from "react-router-dom";
import MdModeEdit, MdDeleteForever from "react-icons/md";
import styles from "./TaskItem.module.scss";
const TaskItem = ( id, name, days, daysDue, overdue, type ) =>
const dispatch = useDispatch();
return (
<article
className=`
$styles.task
$daysDue === 0 ? `$styles.today` : ""
$overdue === true ? `$styles.overdue` : ""
`
>
<Link to="/" className=styles.task_name>
name
</Link>
<span
className=`$styles.task_days $
daysDue === 0 ? `$styles.task_days__today` : ""
$overdue ? `$styles.task_days__overdue` : ""`
>
<strong>type</strong>: days
</span>
<div className=styles.task_buttons>
<button className=`$styles.task_button $styles.edit`>
<MdModeEdit />
</button>
<button
className=`$styles.task_button $styles.delete`
onClick=() => dispatch(removeTask(id))
>
<MdDeleteForever />
</button>
</div>
</article>
);
;
export default TaskItem;
tasks_data.json
:
[
"account_name": "Misty's Gym",
"customer_id": 1,
"days":
"days_due": 1,
"overdue": false
,
"type": "Quotes"
,
"account_name": "Brock's Diner",
"customer_id": 2,
"days":
"days_due": 0,
"overdue": false
,
"type": "Quotes"
,
"account_name": "Samurai Champloo's Fish Bar",
"customer_id": 3,
"days":
"days_due": 5,
"overdue": false
,
"type": "SSL Setup"
,
"account_name": "Tiny Rebel",
"customer_id": 4,
"days":
"days_due": -7,
"overdue": true
,
"type": "Domains"
,
"account_name": "Matalan",
"customer_id": 5,
"days":
"days_due": 13,
"overdue": false
,
"type": "Other"
,
"account_name": "Lowes Soft Drinks",
"customer_id": 6,
"days":
"days_due": 1,
"overdue": false
,
"type": "SEO Setup"
,
"account_name": "Snack 'n' Go",
"customer_id": 7,
"days":
"days_due": -2,
"overdue": true
,
"type": "Quotes"
,
"account_name": "Jeronemo",
"customer_id": 8,
"days":
"days_due": 5,
"overdue": false
,
"type": "Quotes"
,
"account_name": "Tom's Mouse Traps",
"customer_id": 9,
"days":
"days_due": 0,
"overdue": false
,
"type": "Domains"
,
"account_name": "Contiente",
"customer_id": 10,
"days":
"days_due": 2,
"overdue": false
,
"type": "Domains"
,
"account_name": "Um Bongo",
"customer_id": 11,
"days":
"days_due": -1,
"overdue": true
,
"type": "SEO Setup"
]
我到底做错了什么?我还能如何设置状态?
干杯。
【问题讨论】:
我不能确切地告诉你出了什么问题,但我遇到了同样的问题,我的头撞到了墙上几个小时。看看这个函数:redux-toolkit.js.org/api/other-exports#current。我用当前(状态)抛出了一些console.log,并且能够解决我遇到的问题。希望这会有所帮助。 【参考方案1】:这样做:
removeTask: (state, action) =>
const id = action.payload;
const data = state.tasks;
const index = data.findIndex((item) => id === item.id);
data.splice(index, 1)
【讨论】:
好的,也许这会对你有所帮助:***.com/questions/41051302/… 可以尝试将reducer的整个body注释掉,看看是否还是报错 谢谢,我肯定在其他地方有问题。我将不得不进一步研究它并尝试弄清楚。非常感谢您提供的信息。可能应该用谷歌搜索错误消息,但我只是看不出错误是如何来自其他任何地方的。这很可能与我将初始状态映射到列表 idk 的方式有关【参考方案2】:tasksData.sort
(在您的Tasks
组件中)从存储中修改原始tasksData
数组,就像sort
那样。事先制作一个新数组,例如tasksData.concat().sort(...
【讨论】:
啊,是的!我认为这会是好的,因为最终的地图会创建一个新的数组,但我在调用它之前会更改它,当然。这个障碍已经结束,我不再收到突变状态错误,但是现在我无法访问减速器内的状态。state
现在在控制台中显示为代理,并且 state.tasks.data 未定义。多么有趣
见redux-toolkit.js.org/api/… ;)【参考方案3】:
在 reducer 动作中,状态打印为 Proxy 对象(在 Redux Toolkit 中),但是有一个基于 redux-toolkit docs current 的函数,您可以使用它在这样的 reducer 动作中打印状态,例如
import createSlice,current from '@reduxjs/toolkit'
const todoSlice = createSlice(
name: 'todo',
initialState,
reducers:
deleteTodo(state,action)
console.log(current(state));
let f=state.tasks.splice(action.payload,1);
,
,
);
【讨论】:
以上是关于Redux Toolkit:状态在 reducer 中显示为 Proxy / undefined的主要内容,如果未能解决你的问题,请参考以下文章
redux-toolkit 在来自另一个 thunk reducer 的同一切片中使用 actionCreater
createAsyncThunk 和使用 redux-toolkit 编写 reducer 登录
如何使用 redux-toolkit 中的 createSlice 方法在不同的 reducer 函数中使用单个动作类型