我在使用当前正在进行的反应还原方面遇到了一些麻烦。我对Redux来说相对较新,所以也许我在这里错过了一个简单的概念,但我想要做的就是为纸牌游戏构建一个甲板构建应用程序,我希望能够保存任何时候用户在他们的套牌中添加或删除卡片。
但是,只要我点击添加或删除,我就会在尝试发送更新操作时收到以下错误消息。
错误消息如下:
Uncaught Error: A state mutation was detected between dispatches, in the path `decks.0.cards.mainboard.0.quantity`. This may cause incorrect behavior.
我的容器组件
import React, {PropTypes} from 'react';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import DeckMobileDisplay from './DeckMobileDisplay';
import * as deckActions from '../../actions/deckActions';
export class DeckEditorContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
deck: Object.assign({}, this.props.deck)
}
this.addCard = this.addCard.bind(this);
this.removeCard = this.removeCard.bind(this);
}
addCard(board, cardName) {
let deck = this.state.deck;
let cards = this.state.deck.cards;
cards[board].forEach(i => {
if(i.name === cardName)
i.quantity += 1;
});
const update = Object.assign(deck, cards);
this.props.deckActions.updateDeck(update).then(deck => {
console.log(deck);
})
.catch(err => {
console.log(err);
});
}
removeCard(board, cardName) {
let deck = this.state.deck;
let cards = this.state.deck.cards;
cards[board].forEach(i => {
if(i.name === cardName) {
if (i.quantity === 1) {
cards[board].splice(cards[board].indexOf(i), 1);
}
else {
i.quantity -= 1;
}
}
});
const update = Object.assign(deck, cards);
this.props.deckActions.updateDeck(update).then(deck => {
console.log(deck);
})
.catch(err => {
console.log(err);
});
}
render() {
const deck = Object.assign({}, this.props.deck);
return (
<div className="editor-container">
<DeckMobileDisplay
deck={deck}
addCard={this.addCard}
removeCard={this.removeCard}
/>
</div>
);
}
}
DeckEditorContainer.PropTypes = {
deck: PropTypes.object
};
function getDeckById(decks, id) {
const deck = decks.filter(deck => deck.id == id);
if (deck.length) return deck[0];
return null;
}
function mapStateToProps(state, ownProps) {
const deckId = ownProps.params.id;
let deck = {
id: '',
userId: '',
cards: []
}
if (state.decks.length > 0) {
deck = getDeckById(state.decks, deckId);
}
return {
deck: deck
};
}
function mapDispatchToProps(dispatch) {
return {
deckActions: bindActionCreators(deckActions, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(DeckEditorContainer);
DeckMobileDisplay的组件
import React, {PropTypes} from 'react';
import TabContainer from '../common/Tabs/TabContainer';
import Tab from '../common/Tabs/Tab';
import CardSearchContainer from '../CardSearch/CardSearchContainer';
import DeckList from './DeckList.js';
class DeckMobileDisplay extends React.Component {
render() {
return (
<TabContainer>
<Tab title="DeckList">
<DeckList
deck={this.props.deck}
addCard={this.props.addCard}
removeCard={this.props.removeCard}
/>
</Tab>
<Tab title="Search">
<CardSearchContainer
addCard={this.props.addCard}
removeCard={this.props.removeCard}
/>
</Tab>
<Tab title="Stats">
<p>stats coming soon...</p>
</Tab>
</TabContainer>
);
}
}
DeckMobileDisplay.propTypes = {
deck: PropTypes.object.isRequired,
addCard: PropTypes.func.isRequired,
removeCard: PropTypes.func.isRequired
}
export default DeckMobileDisplay;
相关操作
export function createDeck(deck) {
return dispatch => {
dispatch(beginAjaxCall());
const config = {
method: 'POST',
headers: { 'Content-Type' : 'application/json' },
body : JSON.stringify({deck: deck})
};
return fetch(`http://localhost:3000/users/${deck.userId}/decks`, config)
.then(res => res.json().then(deck => ({deck, res})))
.then(({res, deck}) => {
if (res.status >= 200 && res.status < 300) {
dispatch(createDeckSuccess(deck.deck));
}
else
dispatch(createDeckFailure(deck));
})
.catch(err => {
console.log(err);
dispatch(ajaxCallError(err));
});
};
}
export function updateDeck(deck) {
return dispatch => {
dispatch(beginAjaxCall());
const body = JSON.stringify({deck: deck});
const config = {
method: 'PUT',
headers : { 'Content-Type' : 'application/json' },
body: body
};
return fetch(`http://localhost:3000/decks/${deck.id}`, config)
.then(res => res.json().then(deck => ({deck, res})))
.then(({res, deck}) => {
if (res.status >= 200 && res.status < 300) {
dispatch(updateDeckSuccess(deck.deck));
}
dispatch(ajaxCallError(err));
})
.catch(err => {
console.log(err);
dispatch(ajaxCallError(err));
});
};
}
export function updateDeckSuccess(deck) {
return {
type: types.UPDATE_DECK_SUCCESS,
deck
};
}
我的甲板减速机
import * as types from '../actions/actionTypes';
import initialState from './initialState';
export default function deckReducer(state = initialState.decks, action) {
switch (action.type) {
case types.LOAD_USERS_DECKS_SUCCESS:
return action.decks;
case types.CREATE_DECK_SUCCESS:
return [
...state,
Object.assign({}, action.deck)
]
case types.UPDATE_DECK_SUCCESS:
return [
...state.filter(deck => deck.id !== action.deck.id),
Object.assign({}, action.deck)
]
default:
return state;
}
}
如果你需要查看更多应用程序,那么repo就在这里:
https://github.com/dgravelle/magic-redux
感谢任何形式的帮助,谢谢!
答案 0 :(得分:0)
您的问题是由于您手动修改组件的状态而引起的。 Redux的一个原则是:
状态为只读
改变状态的唯一方法是发出一个动作,一个对象 描述发生的事情。
这确保了视图和网络回调都不会 永远写到国家。相反,他们表达了一种意图 改变国家。因为所有变化都是集中的并且发生 一个接一个严格的命令,没有微妙的竞争条件 提防。由于动作只是普通对象,因此可以记录它们, 序列化,存储,然后重放以进行调试或测试 目的。
在方法removeCard
中,您正在修改状态:
removeCard(board, cardName) {
let deck = this.state.deck;
//This is just a reference, not a clone
let cards = this.state.deck.cards;
cards[board].forEach(i => {
if(i.name === cardName) {
if (i.quantity === 1) {
//Here you are modifying cards, which is a pointer to this.state.deck.cards
cards[board].splice(cards[board].indexOf(i), 1);
}
else {
//Here you are modifying cards, which is a pointer to this.state.deck.cards
i.quantity -= 1;
}
}
});
//... more stuff
}
您可能缺少的一个概念是this.state.deck.cards
是指向数组内存位置的引用/指针。如果你想改变它,你需要克隆它。
一种解决方案可能是克隆原始数组:
removeCard(board, cardName) {
let deck = this.state.deck;
//Here you are cloning the original array, so cards references to a totally different memory position
let cards = Object.assign({}, this.state.deck.cards);
cards[board].forEach(i => {
if(i.name === cardName) {
if (i.quantity === 1) {
cards[board].splice(cards[board].indexOf(i), 1);
}
else {
i.quantity -= 1;
}
}
});
//... more stuff
}
希望它对你有所帮助。