删除嵌套状态的项目会导致在 DOM 中删除不正确的项目
Removing item in nested state results in incorrect item being removed in DOM
我正在从事一个利用 React 和 ReFlux 的项目。目前我只有一家 ReFlux 商店:SubscriptionStore.js
。它包含有关用户订阅的简单详细信息,包括 object 的嵌套数组,其中包含附加到其订阅的每个“标题”的详细信息。
下面是SubscriptionStore.js
背后数据的简单细分:
{
hasThisSubcription: 0
hasThatSubscription: 1
nextPeriodStart: "2015-10-12"
nextYearStartDate: "2016-09-12"
accountTitles: [{
id: "1"
title: "The Name"
createdDate: "2015-09-12 16:17:08"
}, {
id: "2"
title: "Another Name"
createdDate: "2015-09-12 16:17:08"
}, {
id: "3"
title: "Yet Another Name"
createdDate: "2015-09-12 16:17:08"
}]
}
我有一个 React 组件,允许用户更新现有标题的名称、添加新标题或删除标题。添加和重命名进展顺利,但删除行为不正常,我敢肯定这是因为我还没有完全掌握 React/ReFlux.
这是这个主要组件的相关代码:
var React = require('react')
var Reflux = require('reflux')
var SubscriptionActions = require('../../stores/SubscriptionActions.js')
var SubscriptionStore = require('../../stores/SubscriptionStore.js')
module.exports = React.createClass({
mixins: [
Reflux.connect(SubscriptionStore, 'subscriptions')
],
/**
* Add new title
* @param {number} id The Title ID
*/
addTitle() {
SubscriptionActions.addNewTitle()
},
/**
* Remove Title from state using on ID
* @param {number} id The Title ID
*/
removeTitle(id) {
SubscriptionActions.removeNewTitle(id)
},
/**
* Update Title title using ID
* @param {number} id The Title ID
* @param {string} value The new title
*/
saveTitle(id, value) {
SubscriptionActions.updateNewTitle(id, value)
},
render() {
// Check for Title subscriptions and create an editable field for each
var theTitles = []
for (var i = 0; i < this.state.subscriptions.accountTitles.length; i++) {
theTitles.push(
<EditableCapsule
key = {i}
ref = {'title' + i}
removable = {i === 0 ? false : true}
labelValue = ''
clickToRemove = {this.removeTitle.bind(this, i)}
primaryAction = {this.saveTitle.bind(this, i)}
/>
)
}
return (
<div>
<ScrollArea ref='scrollArea'>
{theTitles}
</ScrollArea>
</div>
)
}
})
以及来自 SubscriptionStore.js
的相关代码位:
removeTitle(id) {
this.subscriptions.accountTitles = _.without(this.subscriptions.accountTitles, _.findWhere(this.subscriptions.accountTitles, { id: id }));
this.trigger(this.subscriptions)
}
问题
无论我点击删除哪个标题,删除的总是最后一个 标题。如果我吐出 this.subscriptions.accountTitles
内容的前后,它表明确实从数据中删除了正确的 object,但 React 正在渲染元素,就好像它总是最后一个被删除的项目一样。
知道会发生什么吗?
不要使用 <EditableCapsule key = {i} ...
:key
的全部意义在于成为 React 为更新、删除等目的必须使用的确切元素的唯一标识符。
它在某个数组中的位置与元素本身完全无关,所以:首先将其更改为正确的键:
...
render: function() {
var accountTiles = this.state.subscriptions.accountTitles;
var tileset = accountTiles.map(this.buildTiles);
return <ScrollArea ref='scrollArea'>{ tileset }</ScrollArea>;
},
buildTiles: function(tile, position) {
var identifier = tile.title + title.id;
return (
<EditableCapsule
key = { identifier }
ref = { identifier }
removable = { position !== 0 }
labelValue = ''
clickToRemove = { this.removeTitle.bind(this, tile) }
primaryAction = { this.saveTitle.bind(this, tile) }
/>;
);
},
...
请注意,我们现在也不再基于数组位置进行删除。我们将根据磁贴本身进行移除。出于显而易见的原因。
我正在从事一个利用 React 和 ReFlux 的项目。目前我只有一家 ReFlux 商店:SubscriptionStore.js
。它包含有关用户订阅的简单详细信息,包括 object 的嵌套数组,其中包含附加到其订阅的每个“标题”的详细信息。
下面是SubscriptionStore.js
背后数据的简单细分:
{
hasThisSubcription: 0
hasThatSubscription: 1
nextPeriodStart: "2015-10-12"
nextYearStartDate: "2016-09-12"
accountTitles: [{
id: "1"
title: "The Name"
createdDate: "2015-09-12 16:17:08"
}, {
id: "2"
title: "Another Name"
createdDate: "2015-09-12 16:17:08"
}, {
id: "3"
title: "Yet Another Name"
createdDate: "2015-09-12 16:17:08"
}]
}
我有一个 React 组件,允许用户更新现有标题的名称、添加新标题或删除标题。添加和重命名进展顺利,但删除行为不正常,我敢肯定这是因为我还没有完全掌握 React/ReFlux.
这是这个主要组件的相关代码:
var React = require('react')
var Reflux = require('reflux')
var SubscriptionActions = require('../../stores/SubscriptionActions.js')
var SubscriptionStore = require('../../stores/SubscriptionStore.js')
module.exports = React.createClass({
mixins: [
Reflux.connect(SubscriptionStore, 'subscriptions')
],
/**
* Add new title
* @param {number} id The Title ID
*/
addTitle() {
SubscriptionActions.addNewTitle()
},
/**
* Remove Title from state using on ID
* @param {number} id The Title ID
*/
removeTitle(id) {
SubscriptionActions.removeNewTitle(id)
},
/**
* Update Title title using ID
* @param {number} id The Title ID
* @param {string} value The new title
*/
saveTitle(id, value) {
SubscriptionActions.updateNewTitle(id, value)
},
render() {
// Check for Title subscriptions and create an editable field for each
var theTitles = []
for (var i = 0; i < this.state.subscriptions.accountTitles.length; i++) {
theTitles.push(
<EditableCapsule
key = {i}
ref = {'title' + i}
removable = {i === 0 ? false : true}
labelValue = ''
clickToRemove = {this.removeTitle.bind(this, i)}
primaryAction = {this.saveTitle.bind(this, i)}
/>
)
}
return (
<div>
<ScrollArea ref='scrollArea'>
{theTitles}
</ScrollArea>
</div>
)
}
})
以及来自 SubscriptionStore.js
的相关代码位:
removeTitle(id) {
this.subscriptions.accountTitles = _.without(this.subscriptions.accountTitles, _.findWhere(this.subscriptions.accountTitles, { id: id }));
this.trigger(this.subscriptions)
}
问题
无论我点击删除哪个标题,删除的总是最后一个 标题。如果我吐出 this.subscriptions.accountTitles
内容的前后,它表明确实从数据中删除了正确的 object,但 React 正在渲染元素,就好像它总是最后一个被删除的项目一样。
知道会发生什么吗?
不要使用 <EditableCapsule key = {i} ...
:key
的全部意义在于成为 React 为更新、删除等目的必须使用的确切元素的唯一标识符。
它在某个数组中的位置与元素本身完全无关,所以:首先将其更改为正确的键:
...
render: function() {
var accountTiles = this.state.subscriptions.accountTitles;
var tileset = accountTiles.map(this.buildTiles);
return <ScrollArea ref='scrollArea'>{ tileset }</ScrollArea>;
},
buildTiles: function(tile, position) {
var identifier = tile.title + title.id;
return (
<EditableCapsule
key = { identifier }
ref = { identifier }
removable = { position !== 0 }
labelValue = ''
clickToRemove = { this.removeTitle.bind(this, tile) }
primaryAction = { this.saveTitle.bind(this, tile) }
/>;
);
},
...
请注意,我们现在也不再基于数组位置进行删除。我们将根据磁贴本身进行移除。出于显而易见的原因。