简体   繁体   English

Redux 工具包:在 reducer 中显示为 Proxy / undefined 的状态

[英]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.

相关问题 Redux-toolkit:state 是 createSlice reducer 中的代理 - Redux-toolkit: state is a proxy inside a createSlice reducer 为什么我的 Redux Toolkit 减速器状态会发生变化? - Why is my Redux Toolkit reducer mutating state? Redux 工具包:state 显示为代理/[[处理程序]]:null,[[目标]]:null,[[IsRevoked]]:true - Redux Toolkit: state showing as Proxy / [[Handler]]: null, [[Target]]: null, [[IsRevoked]]:true 在reducer USINT Redux中替换整个状态 - Replacing the whole state within a reducer usint Redux 反应 | Redux 工具包共享 State 未定义 - React | Redux Toolkit Shared State Undefined Redux-Toolkit createAsyncThunk Dispatch 显示为未定义 - Redux-Toolkit createAsyncThunk Dispatch is showing as undefined 是否可以使用 Redux Toolkit 从另一个减速器函数(在同一切片内)调用减速器函数? - Is it possible to call a reducer function from another reducer function (within the same slice) using Redux Toolkit? 反应:减少操作导致 Redux 减速器中的未定义状态 - React: Decrease action causes undefined state in Redux reducer Redux preloadedState 导致默认减速器状态的覆盖(以及随后的未定义错误)? - Redux preloadedState causing override (and subsequent undefined error) of default reducer state? 还原器完成后,状态为redux的数组未定义 - redux Array in state got undefined after reducer finish
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM