先看下面代码片段,Notification.add()
当,notice 的数量超过最大数量时,Antd 是把第一个的 key 借给最新插入的这个 notice 使用,做法是保留 key,赋值给新插入的 notice,它自己的 key 变成 userPassKey,然后,notices 数组里面删除老的第一个,新的插入末尾,简单说就是1,2,3,4-> 2,3,4,1, 用了shift()
方法,代码如下。
add = (originNotice: NoticeContent, holderCallback?: HolderReadyCallback) => {
const key = originNotice.key || getUuid();
const notice: NoticeContent & { key: React.Key; userPassKey?: React.Key } = {
...originNotice,
key,
};
const { maxCount } = this.props;
this.setState((previousState: NotificationState) => {
const { notices } = previousState;
const noticeIndex = notices.map((v) => v.notice.key).indexOf(key);
const updatedNotices = notices.concat();
if (noticeIndex !== -1) {
updatedNotices.splice(noticeIndex, 1, { notice, holderCallback });
} else {
if (maxCount && notices.length >= maxCount) {
// XXX, use key of first item to update new added (let React to move exsiting
// instead of remove and mount). Same key was used before for both a) external
// manual control and b) internal react ‘key‘ prop , which is not that good.
// eslint-disable-next-line no-param-reassign
// zombieJ: Not know why use `updateKey`. This makes Notice infinite loop in jest.
// Change to `updateMark` for compare instead.
// https://github.com/react-component/notification/commit/32299e6be396f94040bfa82517eea940db947ece
notice.key = updatedNotices[0].notice.key as React.Key;
notice.updateMark = getUuid();
// zombieJ: That‘s why. User may close by key directly.
// We need record this but not re-render to avoid upper issue
// https://github.com/react-component/notification/issues/129
notice.userPassKey = key;
updatedNotices.shift();
}
updatedNotices.push({ notice, holderCallback });
}
return {
notices: updatedNotices,
};
});
};
而在CSSMotionList
里面是如何体现这一变化的呢。它里面注意是采用diff
算法来发现改变,它的 diff 算法的时间复杂度是O(N),跟React的diff
算法一样。它是有如下假设的,列表里面的数据注意是用来添加删除的,基本上可以看出是无须的,举个简单的例子。1,2,3 变成了1,3,4 算法会翻译成1:keep,2:Delete,3:Keep,4:Add。对于特例,1,2,3 变成2,3,1,它会先翻译成2:Add,3:Add,1:Keep,2:Delete,3:Delete, 然后进一步修正成 2:Add,3:Add,1:Keep,然后再次修正成2:Keep,3:Keep,1:Keep。所以对于调换顺序它的做法有点不那么自然,看如下代码。
export function diffKeys(
prevKeys: KeyObject[] = [],
currentKeys: KeyObject[] = []
) {
let list: KeyObject[] = [];
let currentIndex = 0;
const currentLen = currentKeys.length;
const prevKeyObjects = parseKeys(prevKeys);
const currentKeyObjects = parseKeys(currentKeys);
// Check prev keys to insert or keep
prevKeyObjects.forEach((keyObj) => {
let hit = false;
for (let i = currentIndex; i < currentLen; i += 1) {
const currentKeyObj = currentKeyObjects[i];
if (currentKeyObj.key === keyObj.key) {
// New added keys should add before current key
if (currentIndex < i) {
list = list.concat(
currentKeyObjects
.slice(currentIndex, i)
.map((obj) => ({ ...obj, status: STATUS_ADD }))
);
currentIndex = i;
}
list.push({
...currentKeyObj,
status: STATUS_KEEP,
});
currentIndex += 1;
hit = true;
break;
}
}
// If not hit, it means key is removed
if (!hit) {
list.push({
...keyObj,
status: STATUS_REMOVE,
});
}
});
// Add rest to the list
if (currentIndex < currentLen) {
list = list.concat(
currentKeyObjects
.slice(currentIndex)
.map((obj) => ({ ...obj, status: STATUS_ADD }))
);
}
/**
* Merge same key when it remove and add again:
* [1 - add, 2 - keep, 1 - remove] -> [1 - keep, 2 - keep]
*/
const keys = {};
list.forEach(({ key }) => {
keys[key] = (keys[key] || 0) + 1;
});
const duplicatedKeys = Object.keys(keys).filter((key) => keys[key] > 1);
duplicatedKeys.forEach((matchKey) => {
// Remove `STATUS_REMOVE` node.
list = list.filter(
({ key, status }) => key !== matchKey || status !== STATUS_REMOVE
);
// Update `STATUS_ADD` to `STATUS_KEEP`
list.forEach((node) => {
if (node.key === matchKey) {
// eslint-disable-next-line no-param-reassign
node.status = STATUS_KEEP;
}
});
});
return list;
}
Notification.newInstance(props,callback)
,这个时候在整个 React VirtualDom 创建了一个树,这颗树跟我们的 App 的树地位一样,它们应该与 App 树放在 React 内部同一个数组里面. props 里面可以配置 notification 容器大小,以及,getContainer来指定容器挂载在 Dom 的位置等。callback.notice(noticeProps)
来打开一个 noticie. 它会往容器的 states 里面插入一条新的 notice,可以参考上面的代码。Notification
会被 React 触发检查更新。CSSMotionList 会发现更新后的 notices list。然后新添加的 notice 会变成一个新的 Motion,而它里面套着正在的 notice。notice 的状态是 ADD(diff) 或者 Keep 的 Motion 对应的 props 的 visible=true,动画开始.原文:https://www.cnblogs.com/kongshu-612/p/14940782.html