我在基于React的项目中使用Redux和Immutable.js(基于React Boilerplate构建)并且我正在寻找一种更新或添加到Immutable.js列表的惯用方法
我目前的设置。州最初看起来像这样:
const initialState = fromJS({
accounts: [],
activeAccount: null,
loadedAccounts: [],
});
我有一个account
对象的不可变记录:
const account = new Record({
description: '',
id: '',
name: '',
status: '',
});
我的减速机:
function reducer(state = initialState, action) {
switch (action.type) {
case LOAD_ACCOUNT_SUCCESS:
return state
.set('activeAccount', new account(action.account))
default:
return state;
}
}
这很好用 - 当LOAD_ACCOUNT_SUCCESS
操作被触发时,activeAccount
会更新为action.account
的值。
我可以对此进行修改,以便每次新的LOAD_ACCOUNT_SUCCESS
操作都会将新加载的帐户数据推送到loadedAccounts
:
function reducer(state = initialState, action) {
switch (action.type) {
case LOAD_ACCOUNT_SUCCESS:
const loadedAccount = new account(action.account);
return state
.update('loadedAccounts', (loadedAccounts) => loadedAccounts.push(loadedAccount));
default:
return state;
}
}
但是,此时加载相同的帐户数据两次将导致每次将新记录推送到我的列表(复制数据)。我想要做的是将 添加action.account
添加到loadedAccounts
(现在发生)或更新列表中的记录(如果有)匹配ID。我正在查看类似的问题和可悲的Immutable.js文档,但我无法看到如何执行此操作:没有语法我尝试过如我所期望的那样。
答案 0 :(得分:2)
那么,这里你需要的是嵌套更新。
首先,您必须检查您已加载的帐户列表是否有此帐户。
其次,您必须更改activeAccount
字段。
最后,将帐户添加(或更新)到loadedAccounts
。
这里需要注意的是你如何通过account
财产。如果你从某个地方派生它并以Record
传递,你可以通过===
(或.equals())进行比较,但它似乎只是一个普通的javascript对象 - 我&# 39;稍后会假设它。
就代码而言,它将是:
// we can do it by different ways, it is just one of them
const listWithLoadedAccounts = state.get('loadedAccounts');
const isAccountAlready = Boolean(
listWithLoadedAccounts.filter(
account => account.get('id') === action.account.id
).size
);
const patchedState = state.set('activeAccount', action.account.id);
return isAccountAlready
? patchedState.updateIn(['loadedAccounts'], list => list.map(account => account.get('id') === account.action.id ? new account(action.account) : account))
: patchedState.updateIn(['loadedAccounts'], list => list.concat(new account(action.account)))
这不是理想的代码,可以进行重复数据删除,但是您可以理解 - 如果您需要更改嵌套字段或数据结构,请始终使用深度合并/更新。
您也可以直接设置新字段,例如:
const oldList = state.get('loadedAccounts');
const newList = oldList.concat(action.account);
const patchedState = state.set('loadedAccounts', newList);
但是我个人发现它并不灵活且不一致,因为执行深度合并是非常常见的操作。
答案 1 :(得分:0)
我希望这个例子有所帮助,我正在创建一个新的不可变列表,并首先执行更新,然后添加一个新元素。我没有传递我想要替换的对象,但是你也可以传递你现有的对象,同样在更新方法中你可以访问当前项目
class Test {
a = null;
b = null;
constructor(a,b){
this.a=a;
this.b=b;
}
}
$("#test").html("");
function logme(item){
$("#test").append("<br/>"+JSON.stringify(item));
}
function logmeNewLine(){
$("#test").append("<br/>");
}
function listAddUpadte(key){
var index= list.get('data').findIndex(listing => {
return listing.a === key;
});
logme(' found index (-1 for not found) : ' + index);
if(index >= 0){
logme("upadte");
list = list.set("data",list.get("data").update(index,function(item){
return new Test(key,"go");
}));
}else {
logme("add");
list = list.set("data",list.get("data").push(new Test(key,"go")));
}
list.get('data').forEach(item=>{
logme(item);
});
}
var list = Immutable.fromJS({
"data":[new Test(6,"abhi"),new Test(4,"raj"),new Test(1,"ajay")]
});
logme("intial data");
list.get('data').forEach(item=>{
logme(item);
});
logmeNewLine();
logme("testing replace with a = 4 ")
logmeNewLine();
listAddUpadte(4);
logmeNewLine();
logme("testing add with a = 8 ")
logmeNewLine();
listAddUpadte(8);
logmeNewLine();
logmeNewLine();
logmeNewLine();
<script src="https://cdnjs.cloudflare.com/ajax/libs/immutable/3.7.2/immutable.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="test"></div>
答案 2 :(得分:0)
不是直接答案,而是一个有关如何使用immutable.js添加到列表的示例:
let oldList = List([ 1, 2, 3, 4 ])
let newList = oldList.push(5)