[英]Redux Toolkit: state showing as Proxy / undefined within reducer
Solved it, thanks!解决了,谢谢! - For anyone interested, I was trying to access state.tasks.data within the reducer however due to scope, I could access it via state.data as I was already within the tasks slice.
- 对于任何感兴趣的人,我试图在 reducer 中访问 state.tasks.data 但是由于范围,我可以通过 state.data 访问它,因为我已经在 tasks 切片中。
Edit: My issue with mutated state error is now fixed, it was due to sorting the state directly without creating a new array from it.编辑:我的突变状态错误问题现在已修复,这是由于直接对状态进行排序而没有从中创建新数组。 That has been fixed with
concat()
before sort()
.这已在
sort()
concat()
修复。
My new issue is that state
within my removeTask reducer
is no longer accessible.我的新问题是我的
removeTask reducer
中的state
不再可访问。 It's now returning undefined.它现在返回未定义。 If I
console.log(state)
then it will return a Proxy
with:如果我使用
console.log(state)
那么它将返回一个Proxy
:
[[Handler]]: null,
[[Target]]: null,
[[IsRevoked]]:true
Edit 2: I've found that the Proxy is due to immer
under the hood which does something with the mutation to use it in a way that the state
is not mutated.编辑 2:我发现代理是由于
immer
在引擎盖下,它对突变做了一些事情,以一种state
不突变的方式使用它。 I've still yet to fix the issue of state.tasks.data
returning undefined
.我还没有解决
state.tasks.data
返回undefined
的问题。
I'm using react-redux and redux-toolkit.我正在使用 react-redux 和 redux-toolkit。 I am just learning Redux so my head is fried, haven't a clue.
我只是在学习 Redux,所以我的脑袋炸了,一点头绪都没有。
I've followed some information from the redux-toolkit basic tutorial on their website which says that you can mutate state in the reducers because toolkit does something behind the scenes to stop it actually mutating the state:我遵循了他们网站上 redux-toolkit 基本教程中的一些信息,其中说你可以在 reducer 中改变状态,因为工具包在幕后做了一些事情来阻止它实际改变状态:
https://redux-toolkit.js.org/tutorials/basic-tutorial https://redux-toolkit.js.org/tutorials/basic-tutorial
In any case, the counter example they provide mutates the state.在任何情况下,他们提供的反例都会改变状态。 The increment reducer returns
state += 1
- this works fine increment reducer 返回
state += 1
- 这很好用
Now I've got my own thing going where I have the initial state
set to an array of objects I've pulled in. See below:现在我有了自己的东西,我将
initial state
设置为我拉入的一组对象。见下文:
tasksSlice.js
: 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;
Now I've got the tasks listed out in my tasks
component.现在我已经在我的
tasks
组件中列出了任务。 Each task is listed using a taskItem
component.每个任务都使用
taskItem
组件列出。 Inside the taskItem
component I have a delete button which I have set the onClick
event to use the removeTask
reducer.在
taskItem
组件内,我有一个删除按钮,我已将它的onClick
事件设置为使用removeTask
reducer。
<button
className={`${styles.task_button} ${styles.delete}`}
onClick={() => dispatch(removeTask(id))}
>
<MdDeleteForever />
</button>
This is passing the "customer_id" field from the data
that's mapped out into the task list from initial state
.这是将
data
中的“customer_id”字段从initial state
映射到任务列表中。
I want to be able to delete the task so I'm trying to mutate it (as toolkit said I can) by filtering the state.tasks.data
and returning everything apart from the task where the id
is a match.我希望能够删除任务,所以我试图通过过滤
state.tasks.data
并返回除id
匹配的任务之外的所有内容来改变它(正如工具包所说的那样)。
Unfortunately I keep getting an error message:不幸的是,我不断收到一条错误消息:
Error: Invariant failed: A state mutation was detected between dispatches, in the path 'tasks.data.0'. This may cause incorrect behavior.
tasks.js
- Tasks container component (quite messy): 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.offsetWidth}px`;
activeBar.style.transform = `translate(${active.offsetLeft}px, ${active.offsetTop}px)`;
}
});
const setActive = (e, type) => {
setTaskTab({ activeTask: type });
setSize(false);
const activeBar = document.querySelector(".active_bar");
activeBar.style.width = `${e.target.offsetWidth}px`;
activeBar.style.transform = `translate(${e.target.offsetLeft}px, ${e.target.offsetTop}px)`;
};
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
: 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
: 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"
}
]
What on earth am I doing wrong?我到底做错了什么? How else can I set the state?
我还能如何设置状态?
Cheers.干杯。
tasksData.sort
(in your Tasks
component) modifies the original tasksData
array from the store, as sort
does that. tasksData.sort
(在您的Tasks
组件中)修改存储中的原始tasksData
数组,就像sort
那样。 Make a new array out of it beforehand, eg by tasksData.concat().sort(...
事先用它创建一个新数组,例如通过
tasksData.concat().sort(...
also please reminde inside a reducer action, the state prints as a Proxy object, in redux toolkit.还请提醒在 reducer 操作中,在 redux 工具包中,状态打印为 Proxy 对象。 but there is function base on redux-toolkit dosc
current
that you can use to print your state inside reducer action like this eg但是有一个基于 redux-toolkit dosc
current
函数,你可以用它来打印你在减速器动作中的状态,例如
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)
},
},
});
Do it like this:像这样做:
removeTask: (state, action) => {
const { id } = action.payload;
const { data } = state.tasks;
const index = data.findIndex((item) => id === item.id);
data.splice(index, 1)
}
The following code worked for me:以下代码对我有用:
import {current } from '@reduxjs/toolkit'
{...
console.log(current(state));
...
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.