引言
在日常使用redux
的時候,當view
發出一個action
請求並被對應的reducer
處理時,有時候store中對應的state值已經變了,但view中未進入componentWillReceiveProps
這個生命週期。本文主要解釋下這種現象的發生到底是因為什麼。
值引用
簡單值(即標量基本型別值),包含的資料型別有null,undefined,字串,數字,布林值和ES6中的symbol。
例子
const a = 1;
let b = a;
b === a // true
複製程式碼
特點:被賦值的變數改變時,賦值變數的值不會隨之改變
const nickname = 'ssx';
let lastname = nickname;
lastname === nickname // true;
lastname = 'Mr.oldwang';
nickname //'ssx'
複製程式碼
引用賦值
針對複合物件(陣列,物件,函式)
例子
- 物件
const user1 = { nickname: 'ssx', age: 23 }
let user2 = user1;
user2; // { nickname: 'ssx', age: 23 }
user1 === user 2; // true
user2.nickname = 'gdd';
user1.nickname = 'gdd';
複製程式碼
- 陣列
const userIdlist = [1,2,3,4];
let newUserIdlist = userIdlist;
newUserIdlist === userIdlist; // false
newUserIdlist[0] = 2;
newUserIdlist; // [2,2,3,4]
userIdlist; // [2,2,3,4]
複製程式碼
如果資料結構隨著專案,複雜度也隨之提升。引用賦值可能會導致預期效果和實際不一致。
針對實際React專案談談引用賦值帶來的問題
1.redux的listening方法不會被觸發,從而不會執行mapStateToProps, view不會重新整理
2.不當的引用賦值導致的未dispatch一個action就已經修改了對應store中的值
複製程式碼
分別根據問題,寫幾個實際例子
針對1中的例子
// info = {id: 10023, nickname: 'ssx_real', age: 40}
function userlistReducer(
state = [ {id: 10023, nickname: 'ssx', age: 23} ],
action ={}
) {
const { type, info } = action;
case "GET_USERINFO_LOADING":
return state;
case "GETREAL_USERINFO_SUCCESS":
{
const userlist = state;
const fakeUserinfo = userlist.find(u => u.id === info.id);
if (fakeUserinfo) {
fakeUserinfo.nickname = info.nickname;
fakeUserinfo.age = info.age;
}
return [...userlist];
}
case ... // 其他省略
default: return state;
}
複製程式碼
上面這種寫法導致的現象是想要將原來fake的資料修改成通過網路獲取的真資料,但在return之前就已經修改了目標物件的值,即使dispatch後store中對應值修改了,也不是我們想要的效果。
prevprops userlist: [{id: 10023, nickname: 'ssx_real', age: 40}]
action: GETREAL_USERINFO_SUCCESS
nextprops userlist: [{id: 10023, nickname: 'ssx_real', age: 40}]
複製程式碼
改寫
// realInfo = {id: 10023, nickname: 'ssx_real', age: 40}
function userlistReducer(
state = [ {id: 10023, nickname: 'ssx', age: 23} ],
action ={}
) {
const { type, realInfo } = action;
case "GET_USERINFO_LOADING":
return state;
case "GETREAL_USERINFO_SUCCESS":
{
const userlist = state;
const fakeUserinfo = {...userlist.find(u => u.id === realInfo.id)};
if (fakeUserinfo) {
fakeUserinfo.nickname = realInfo.nickname;
fakeUserinfo.age = realInfo.age;
}
return [
...userlist.filter(u => u.id !== realInfo.id),
...[fakeUserinfo]
]
}
case ... // 其他省略
default: return state;
}
複製程式碼
針對2中的例子
componentWillReceiveProps(nextProps) {
// todo something...
for (let item of newProps.groupRooms[this.state.roomID]) {
let item = {};
item.obj = item
item.obj.lastRecord = nextProps.chatData.chatlist.lastRecord
roomsData.push(item);
}
this.setState({ roomsData });
// todo something...
}
複製程式碼
上面的例子本意沒打算修改對應groupRooms[this.state.roomID]的值 但是由於用了引用賦值導致間接修改其值。這就是上面說的第2中情況,未dispatch一個action就已經修改了對應store中的值。
改寫
componentWillReceiveProps(nextProps) {
// todo something...
for (let item of newProps.groupRooms[this.state.roomID]) {
let item = {};
item.obj = {...item};
item.obj.lastRecord = {...nextProps.chatData.chatlist.lastRecord}
roomsData.push(item);
}
this.setState({ roomsData });
// todo something...
}
複製程式碼
關於根據不同資料型別解決引用賦值的一些方法:
- es6 擴充套件運算子
...
- 陣列
concat()
[...arr1,...arr2]
arr1 = new Array(...arr2)
- 物件
Object.assign({}, obj1)
obj2 = {...obj1}
map
集合m1 = new Map() m2.forEach((v,k) => m1.set(k, v))
這些方法主要是進行了淺copy,物件的key被copy時,key的引用的物件不會被複制
尾語
如果你的專案中有引用賦值導致的問題,希望本文章會對你有所幫助~
複製程式碼