![](/img/trans.png)
[英]Redux-toolkit: state is a proxy inside a createSlice reducer
[英]Redux Toolkit: state showing as Proxy / undefined within reducer
解決了,謝謝! - 對於任何感興趣的人,我試圖在 reducer 中訪問 state.tasks.data 但是由於范圍,我可以通過 state.data 訪問它,因為我已經在 tasks 切片中。
編輯:我的突變狀態錯誤問題現在已修復,這是由於直接對狀態進行排序而沒有從中創建新數組。 這已在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
在任何情況下,他們提供的反例都會改變狀態。 increment reducer 返回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
reducer。
<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.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
:
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"
}
]
我到底做錯了什么? 我還能如何設置狀態?
干杯。
tasksData.sort
(在您的Tasks
組件中)修改存儲中的原始tasksData
數組,就像sort
那樣。 事先用它創建一個新數組,例如通過tasksData.concat().sort(...
還請提醒在 reducer 操作中,在 redux 工具包中,狀態打印為 Proxy 對象。 但是有一個基於 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)
},
},
});
像這樣做:
removeTask: (state, action) => {
const { id } = action.payload;
const { data } = state.tasks;
const index = data.findIndex((item) => id === item.id);
data.splice(index, 1)
}
以下代碼對我有用:
import {current } from '@reduxjs/toolkit'
{...
console.log(current(state));
...
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.