简体   繁体   English

由于状态突变,Reducer中未捕获的Redux动作

[英]Redux action uncaught in reducer because of mutated state

You can assume the following variables: 您可以假设以下变量:

click = true

categories = [
  {title: "all", onClick: false},
  {title: "video", onClick: false},
  {title: "gif", onClick: false},
  {title: "website", onClick: false}
]

finalCategories = [
  {title: "all", onClick: true},
  {title: "video", onClick: true},
  {title: "gif", onClick: true},
  {title: "website", onClick: true}
]

I have a set of redux actions here: 我在这里有一组redux动作:

import * as actionTypes from './actionTypes';
import {beginAjaxCall, ajaxCallError} from './ajaxStatusActions';
import * as Utils from './utils/dashboardActionsUtils';

export function updateCategoriesSuccess(categories) {
  return {type: actionTypes.UPDATE_CATEGORIES_SUCCESS, categories};
}

export function categoryAllClick(categories, click) {
  return function(dispatch) {
    dispatch(beginAjaxCall());
    const finalCategories = Utils.updateMenuOnAll(categories, click);
    dispatch(updateCategoriesSuccess(finalCategories));
  };
}

Where: 哪里:

export function updateMenuOnAll(menu, click) {
  const finalMenu = Object.assign([], menu);
  if (click) {
    finalMenu.map(item => item.onClick = true);
  } else {
    finalMenu.map(item => item.onClick = false);
  }
  return finalMenu;
}

However, when I call categoryAllClick(categories, click) the actionTypes.UPDATE_CATEGORIES_SUCCESS does not get caught by my reducer: 但是,当我调用categoryAllClick(categories, click)actionTypes.UPDATE_CATEGORIES_SUCCESS不会被我的减速器捕获:

import * as actionTypes from '../actions/actionTypes';
import initialState from './initialState';

export default function dashboardReducer(state = initialState.dashboard, action) {
  switch(action.type) {
    case actionTypes.UPDATE_CATEGORIES_SUCCESS:
      return Object.assign({}, state, {categories: action.categories});

    default:
      return state;
  }
}

I am assuming this is because when I attempt this action, I get the error message: 我假设这是因为当我尝试执行此操作时,收到错误消息:

2 Uncaught Error: A state mutation was detected between dispatches, in the path `dashboard.categories.0.onClick`. This may cause incorrect behavior. (http://redux.js.org/docs/Troubleshooting.html#never-mutate-reducer-arguments)

Is there something I am doing wrong in the updateMenuOnAll exported function to cause this state mutation? 我在updateMenuOnAll导出的函数中是否做错了导致这种状态突变的事情? I thought using Object.assign() would avoid this issue. 我认为使用Object.assign()可以避免此问题。 Thanks! 谢谢!

EDIT 编辑

The following react component is the one calling categoryAllClick , when the appropriate div is clicked: 当单击适当的div时,以下react组件是一个调用categoryAllClick

import React from 'react';
import PropTypes from 'prop-types';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import * as dashboardActions from '../../../../../actions/dashboardActions';

class CheckboxItem extends React.Component {
  constructor() {
    super();
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    const {menu, title, type, categoryAllClick, tagAllClick, actions} = this.props;
    if (type === "categories") {
      if (title === "all") {
        actions.categoryAllClick(menu, !categoryAllClick);
      } else {
        actions.updateCategories(title, menu);
      }
    } else if (type === "tags") {
      if (title === "all") {
        actions.tagAllClick(menu, !tagAllClick);
      } else {
        actions.updateTags(title, menu);
      }
    }
  }

  render() {
    return (
      <div className="div-common-menu-item" onClick={this.handleClick}>
        // Child componenets here
      </div>
    );
  }
}

CheckboxItem.propTypes = {
  menu: PropTypes.array.isRequired,
  title: PropTypes.string.isRequired,
  type: PropTypes.string.isRequired,
  categories: PropTypes.array.isRequired,
  tags: PropTypes.array.isRequired,
  categoryAllClick: PropTypes.bool.isRequired,
  tagAllClick: PropTypes.bool.isRequired,
  actions: PropTypes.object.isRequired
};

function mapStateToProps(state) {
  return {
    categories: state.dashboard.categories,
    tags: state.dashboard.tags,
    categoryAllClick: state.dashboard.categoryAllClick,
    tagAllClick: state.dashboard.tagAllClick
  };
}

function mapDispatchToProps(dispatch) {
  return {
    actions: bindActionCreators(dashboardActions, dispatch)
  };
}

const connectedStateAndProps = connect(mapStateToProps, mapDispatchToProps);
export default connectedStateAndProps(CheckboxItem);

Your updateMenuOnAll function is wrong, in three ways: 您的updateMenuOnAll函数有以下三种错误:

  • It's mutating the items when you loop over them 当您遍历项目时,它会对其进行变异
  • map() returns a new array, but you're ignoring that return value map()返回一个新数组,但是您忽略了该返回值
  • Using Object.assign() is not the right way to copy an array. 使用Object.assign()不是复制数组的正确方法。

A better approach would be: 更好的方法是:

export function updateMenuOnAll(menu, click) {
  const newOnClick = !!click;

  const finalMenu = menu.map(item => ({...item, onClick : newOnClick}));
  return finalMenu;
}

我没有在函数“ Utils.updateMenuOnAll”中看到您的代码,但是如果它正在更改您的对象类别,则可能会看到该错误。

That's because you should not call categoryAllClick(categories, click) directly, but use a dispatcher instead. 这是因为您不应直接调用categoryAllClick(categories, click) ,而应使用调度程序。 For example, if you are react, using react-redux you "connect" your component (using import { connect } from 'react-redux'; ) passing your function in the mapDispatchToProps argument. 例如,如果您有反应,则使用react-redux “连接”组件(使用import { connect } from 'react-redux'; )在mapDispatchToProps参数中传递函数。

Having that said, you are correct to assume that using Object.assign is not mutating your state. 话虽如此,您正确地假设使用Object.assign不会改变您的状态。

The problem was the updateMenuOnAll function. 问题是updateMenuOnAll函数。 Rewriting it to the below fixed the problem. 将其重写为以下内容可解决此问题。 Thanks everyone! 感谢大家!

export function updateMenuOnAll(menu, click) {
  return menu.map(item => ({title: item.title, onClick: click}));
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM