学习seamless-immutable

作者 likaiqiang 日期 2018-05-24
学习seamless-immutable

为什么需要immutable

redux中reducer根据各个不同的action和旧的state需要返回一个全新的state。什么意思呢,假如有这样一个state

const state = {
arr:[1,2,3,4]
}

我现在要把arr数组的第2项改为10,常规方法:state.arr[1] = 10。但是在react中,所有直接修改state都是不允许的,你需要这样干:


const reducer = (state,action)=>{
const {type,n} = action //n表示改变数组的第几项
const newState = deepClone(state)
newState.arr[n] = 10
switch(type){
case 'change':return newState;break;
....
default:return {...state}
}
}

需要基于action和旧的state重新生成新的state,并返回这个state。很麻烦而且不能防止不小心修改state。所以就诞生了Immutable(react不变性助手),通过immutable初始化的数据具有不变性,所有直接修改原数据的操作都不会真的修改原数据,而是返回一个新的数据。React官方推荐Immutable库有两个:immutable.js和seamless-immutable.js,这里只介绍后者。上面的功能用seamless-immutable实现,需要这么写。

const state = Immutable({
arr:[1,2,3,4]
})

const reducer = (state,action)=>{
const {type,n} = action //n表示改变数组的第几项
switch(type){
case 'change':return Immutable.set(state,'arr',Immutable.flatMap(state.arr,(item,index)=>{
if(index == n) return 10
else return item
}))
....
default:return {...state}
}
}

seamless-immutable的api

通用方法

isImmutable

判断一个对象/数组是不是Immutable(不变性的)

var arr1 = [1,2,3]
var arr2 = Immutable(arr1)
console.log(Immutable.isImmutable(arr1),Immutable.isImmutable(arr2)) //false true

asMutable

把一个Immutable数组/对象转换为mutable

var arr1 = Immutable([1,2,3])
console.log(Immutable.isImmutable(arr1)) //true
var arr2 = Immutable.asMutable(arr1)
console.log(Immutable.isImmutable(arr2)) //false

对象

getIn

getIn(obj,[path1,path2,...],defaultValue)

第一个参数是目标对象。第二个参数是一个路径数组,第三个参数是默认值(如果通过path array找不到对应的值,则会返回这个默认值)

var obj = Immutable({type: {main: "parrot", subtype: "Norwegian Blue"}, status: "alive"});
Immutable.getIn(obj, ["type", "subtype"]);
// returns "Norwegian Blue"
Immutable.getIn(obj, ["type", "class"], "Aves");
// returns "Aves"

merge

var obj = Immutable({status: "good", hypothesis: "plausible", errors: 0});
Immutable.merge(obj, {status: "funky", hypothesis: "confirmed"});
// returns Immutable({status: "funky", hypothesis: "confirmed", errors: 0})

var obj = Immutable({status: "bad", errors: 37});
Immutable.merge(obj, [
{status: "funky", errors: 1}, {status: "groovy", errors: 2}, {status: "sweet"}]);
// returns Immutable({status: "sweet", errors: 2})
// because passing an Array is shorthand for
// invoking a separate merge for each object in turn.

第二个参数可以通过数组来提供多个可合并对象,如果是这样,相同的属性值,后面的会覆盖前面的

set

var obj = Immutable({type: "parrot", subtype: "Norwegian Blue", status: "alive"})
Immutable.set(obj, "status", "dead")

顾名思义,通过key-value改变目标对象某个key的值

setIn

功能与set类似,不同的是,setIn的第二个参数是一个path array,可以做一些深度赋值

update

var obj = Immutable({foo:1,bar:2})
var obj2 = Immutable.update(obj,'foo',(x)=>{
return 100 + x
})
console.log(obj2)
//Immutable({foo:101,bar:2})

对一个Immutable对象的某一项进行更新操作,与set不同的是,它的第三个参数是个函数。这个方法还可以有第4/5/…/n个参数,这些参数作为第三个参数(函数)的实参传递给该函数,看个例子。

var obj = Immutable({foo:1,bar:2})
var obj2 = Immutable.update(obj,'foo',(x,y,z)=>{
return x + y + z
},100,200)
console.log(obj2)
//Immutable({foo:301,bar:2})

updateIn

与update类似,不同的是它的第二个参数是个path array

without

移除目标对象的某一项并返回新的对象。此方法可以接受两个参数。也可以接受三个参数

var obj = Immutable({a:1,b:2,c:3})
var obj2 = Immutable.without(obj,"a","b")
console.log(obj2)
// Immutable({c:3})

上面的代码还可以这样写

var obj = Immutable({a:1,b:2,c:3})
var obj2 = Immutable.without(obj,["a","b"])
console.log(obj2)
// Immutable({c:3})

它的第二个参数还可以是个函数

var obj = Immutable({a:1,b:2,c:3})
var obj2 = Immutable.without(obj,(value,key)=>{
return key == 'a' || value == 2
})
console.log(obj2)
// Immutable({c:3})

数组

flatMap

此方法作用类似于Array的Map方法

var arr = Immutable([1,2,3,4])
var arr2 = Immutable.flatMap(arr,(item,index)=>{
return item * 2
})
console.log(arr2) //Immutable([2,4,6,8])

也可以return 一个数组

var arr = Immutable([1,2,3,4])
var arr2 = Immutable.flatMap(arr,(item,index)=>{
return [item,item * 2]
})
console.log(arr2) //Immutable([1,2,2,4,3,6,4,8])

asObject

这个方法很无聊喽,遍历数组的每一项,返回[“key”:”value”],把这个数组变成一个对象。

var array = Immutable(["hey", "you"]);
Immutable.asObject(array, function(str) {
return [str, str.toUpperCase()];
})
//Immutable({hey: "HEY", you: "YOU"})

数组总结

看起来给数组没提供几个有用的方法,实际flatMap是个很强悍的方法。假如有这样的需求,删除数组的某一项或某几项,我们就可以这样干

var arr = Immutable([1,2,3,4])
var arr2 = Immutable.flatMap(arr,(item,index)=>{
if(index > 0 && index < arr.length-1) return [item]
else return []
})
console.log(arr2) //Immutable([2,3])