# Immutability in React
# Javascript
我们先来看看 Javascript 是怎么实现 Array 和 Object 的不可变性。能用基本的 Javascript 实现的,我们就不需要求助于第三方库。
# Array
Array 中下列常用方法是可变方法,因为它们修改了原数组
push
pop
unshift
shift
splice
sort
reverse
而这些方法不会修改原数组,而是返回一个新数组,是不可变方法
concat
slice
filter
map
...
操作符
我们就使用这些不变方法来代替可变方法,实现 Array 的不可变性
# push
在数组的末尾添加数据项
const array = [1,2,3,4];
const immutableArray = array.concat(5); // 1,2,3,4,5
// 或者
const immutableArray = [...array, 5]; // 1,2,3,4,5
1
2
3
4
2
3
4
# pop
从数组的末尾移除数据项
const array = [1,2,3,4];
const last = array[array.length - 1]; // 4
const immutableArray = array.slice(0, -1); // 1,2,3
1
2
3
2
3
# unshift
在数组的开头添加数据项
const array = [1,2,3,4];
const immutableArray = [5].concat(array); // 5,1,2,3,4
// 或者
const immutableArray = [5, ...array]; // 5,1,2,3,4
1
2
3
4
2
3
4
# shift
在数组的开头移除数据项
const array = [1,2,3,4];
const firt = array[0]; // 1
const immutableArray = array.slice(1); // 2,3,4
1
2
3
2
3
# splice
splice
具有插入、替换、删除的功能
# 插入
const array = [1,2,3,4];
// 在 index = 2,插入数值 5
const index = 2;
const newValue = 5;
const immutableArray = [...array.slice(0, index), newValue, ...array.slice(index)]; // 1,2,5,3,4
1
2
3
4
5
2
3
4
5
# 替换
const array = [1,2,3,4];
// 将 index = 2 的值,替换成数值 5
const index = 2;
const newValue = 5
const immutableArray = [...array.slice(0, index), newValue, ...array.slice(index + 1)]; // 1,2,5,4
// 更好的方法
const immutableArray = array.map((value, idx) => idx !== index ? value : newValue)
1
2
3
4
5
6
7
2
3
4
5
6
7
# 删除
const array = [1,2,3,4];
// 将 index = 2 的值删掉
const index = 2;
const immutableArray = [...array.slice(0, index), ...array.slice(index + 1)]; // 1,2,4
// 更好的方法
const immutableArray = array.filter((_, idx) => idx !== index)
1
2
3
4
5
6
2
3
4
5
6
# Sort and reverse
先使用 ...
浅复制数组,然后在复制得到的数组上调用 sort
、reverse
。
上面提到的方法(比如 push
、pop
等)以及未提及的方法(比如 fill
、copyWithin
等),都可以采用这个方式实现不可变性。
const array = [1,3,2,4];
const sortedArray = [...array].sort(); // 1,2,3,4
const reversedArray = [...array].reverse(); // 4,2,3,1
1
2
3
2
3
# Object
对于浅层次的修改比较简单,使用 ...
操作符或者 assign
方法,例如
const state = {
selected: 'apple',
quantity: 13,
};
const newState = {
...state,
selected: 'orange', // 修改
origin: 'imported from Spain' // 新增
};
/*
newState = {
selected: 'orange',
quantity: 13,
origin: 'imported from Spain'
}
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
对于深层次的修改就比较麻烦了,比如修改 state.a.b.c.d = 10
const state = {
a: {
b: {
c: {
d: 5
}
}
}
};
const newState = {
...state,
...{
a: {
...state.a,
...{
b: {
...state.a.b,
...{
c: {
...state.a.b.c,
d: 10
}
}
}
}
}
}
}
/*
{
a: { b: { c: { d: 5 } } }
}
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
这段代码看得人头皮发麻,对于深层次的修改建议使用 lodash
cloneDeep
(opens new window) 方法,拷贝一份原对象,然后再修改,或者使用第三库
const state = {
a: {
b: {
c: {
d: 5
}
}
}
};
const newState = _.cloneDeep(state)
newState.a.b.c.d = 10
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# Immer
未完待续...