Skip to content

Commit 1d7f895

Browse files
committed
fix for react 17 ant-design/ant-motion#229
1 parent ac7c1ba commit 1d7f895

File tree

2 files changed

+109
-98
lines changed

2 files changed

+109
-98
lines changed

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "rc-queue-anim",
3-
"version": "1.7.0",
3+
"version": "1.8.0",
44
"description": "Queue animation component for react",
55
"keywords": [
66
"react",
@@ -83,7 +83,8 @@
8383
"dependencies": {
8484
"babel-runtime": "6.x",
8585
"prop-types": "^15.6.0",
86-
"rc-tween-one": "^2.5.0"
86+
"rc-tween-one": "^2.5.0",
87+
"react-lifecycles-compat": "^3.0.4"
8788
},
8889
"types": "index.d.ts",
8990
"pre-commit": [

src/QueueAnim.jsx

Lines changed: 106 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import React, { createElement } from 'react';
22
import PropTypes from 'prop-types';
33
import TweenOne, { ticker } from 'rc-tween-one';
4+
import { polyfill } from 'react-lifecycles-compat';
5+
46
import {
57
toArrayChildren,
68
findChildInChildrenByKey,
@@ -54,6 +56,108 @@ class QueueAnim extends React.Component {
5456
onEnd: noop,
5557
appear: true,
5658
};
59+
60+
static getDerivedStateFromProps(props, { prevProps, children, childrenShow: prevChildShow, $self }) {
61+
const nextState = {
62+
prevProps: props,
63+
};
64+
const prevChildren = prevProps ? toArrayChildren(prevProps.children).filter(c => c) : [];
65+
const nextChildren = toArrayChildren(props.children).filter(c => c);
66+
if (prevProps &&
67+
prevChildren.map(c => c.key).join() !== nextChildren.map(c => c.key).join()
68+
) {
69+
let currentChildren = $self.originalChildren.filter(item => item);
70+
if (children.length) {
71+
/**
72+
* 多次刷新处理
73+
* 如果 state.children 里还有元素,元素还在动画,当前子级加回在出场的子级;
74+
*/
75+
const leaveChild = children.filter(
76+
item => item && $self.keysToLeave.indexOf(item.key) >= 0,
77+
);
78+
$self.leaveUnfinishedChild = leaveChild.map(item => item.key);
79+
/**
80+
* 获取 leaveChild 在 state.children 里的序列,再将 leaveChild 和 currentChildren 的重新排序。
81+
* 避逸 state.children 在 leaveComplete 里没全部完成不触发,
82+
* leaveComplete 里如果动画完成了是会删除 keyToLeave,但 state.children 是在全部出场后才触发清除,
83+
* 所以这里需要处理出场完成的元素做清除。
84+
*/
85+
const stateChildren = mergeChildren(currentChildren, children);
86+
const currentChild = [];
87+
const childReOrder = child => {
88+
child.forEach(item => {
89+
const order = stateChildren.indexOf(item);
90+
// -1 不应该出现的情况,直接插入数组后面.
91+
if (order === -1) {
92+
currentChild.push(item);
93+
} else {
94+
currentChild.splice(order, 0, item);
95+
}
96+
});
97+
};
98+
childReOrder(leaveChild);
99+
childReOrder(currentChildren);
100+
currentChildren = currentChild.filter(c => c);
101+
}
102+
const newChildren = mergeChildren(currentChildren, nextChildren);
103+
104+
const childrenShow = !newChildren.length ? {} : prevChildShow;
105+
$self.keysToEnterPaused = {};
106+
const emptyBool = !nextChildren.length && !currentChildren.length && children.length;
107+
/**
108+
* 在出场没结束时,childrenShow 里的值将不会清除。
109+
* 再触发进场时, childrenShow 里的值是保留着的, 设置了 forcedReplay 将重新播放进场。
110+
*/
111+
if (!emptyBool) {
112+
// 空子级状态下刷新不做处理
113+
const nextKeys = nextChildren.map(c => c.key);
114+
$self.keysToLeave.forEach(key => {
115+
// 将所有在出场里的停止掉。避免间隔性出现
116+
if (nextKeys.indexOf(key) >= 0) {
117+
$self.keysToEnterPaused[key] = true;
118+
currentChildren = currentChildren.filter(item => item.key !== key);
119+
if (props.forcedReplay) {
120+
// 清掉所有出场的。
121+
delete childrenShow[key];
122+
}
123+
}
124+
});
125+
}
126+
127+
$self.keysToEnter = [];
128+
$self.keysToLeave = [];
129+
130+
// need render to avoid update
131+
nextState.childrenShow = childrenShow;
132+
nextState.children = newChildren;
133+
134+
nextChildren.forEach(c => {
135+
if (!c) {
136+
return;
137+
}
138+
const key = c.key;
139+
const hasPrev = findChildInChildrenByKey(currentChildren, key);
140+
if (!hasPrev && key) {
141+
$self.keysToEnter.push(key);
142+
}
143+
});
144+
145+
currentChildren.forEach(c => {
146+
if (!c) {
147+
return;
148+
}
149+
const key = c.key;
150+
const hasNext = findChildInChildrenByKey(nextChildren, key);
151+
if (!hasNext && key) {
152+
$self.keysToLeave.push(key);
153+
ticker.clear($self.placeholderTimeoutIds[key]);
154+
delete $self.placeholderTimeoutIds[key];
155+
}
156+
});
157+
$self.keysToEnterToCallback = [...$self.keysToEnter];
158+
}
159+
return nextState;
160+
}
57161
constructor(props) {
58162
super(props);
59163
/**
@@ -133,6 +237,7 @@ class QueueAnim extends React.Component {
133237
this.state = {
134238
children,
135239
childrenShow,
240+
$self: this,
136241
};
137242
}
138243

@@ -142,101 +247,6 @@ class QueueAnim extends React.Component {
142247
}
143248
}
144249

145-
componentWillReceiveProps(nextProps) {
146-
const nextChildren = toArrayChildren(nextProps.children).filter(item => item);
147-
let currentChildren = this.originalChildren.filter(item => item);
148-
if (this.state.children.length) {
149-
/**
150-
* 多次刷新处理
151-
* 如果 state.children 里还有元素,元素还在动画,当前子级加回在出场的子级;
152-
*/
153-
const leaveChild = this.state.children.filter(
154-
item => item && this.keysToLeave.indexOf(item.key) >= 0,
155-
);
156-
this.leaveUnfinishedChild = leaveChild.map(item => item.key);
157-
/**
158-
* 获取 leaveChild 在 state.children 里的序列,再将 leaveChild 和 currentChildren 的重新排序。
159-
* 避逸 state.children 在 leaveComplete 里没全部完成不触发,
160-
* leaveComplete 里如果动画完成了是会删除 keyToLeave,但 state.children 是在全部出场后才触发清除,
161-
* 所以这里需要处理出场完成的元素做清除。
162-
*/
163-
const stateChildrens = mergeChildren(currentChildren, this.state.children);
164-
const currentChild = [];
165-
const childReOrder = child => {
166-
child.forEach(item => {
167-
const order = stateChildrens.indexOf(item);
168-
// -1 不应该出现的情况,直接插入数组后面.
169-
if (order === -1) {
170-
currentChild.push(item);
171-
} else {
172-
currentChild.splice(order, 0, item);
173-
}
174-
});
175-
};
176-
childReOrder(leaveChild);
177-
childReOrder(currentChildren);
178-
currentChildren = currentChild.filter(c => c);
179-
}
180-
const newChildren = mergeChildren(currentChildren, nextChildren);
181-
182-
const childrenShow = !newChildren.length ? {} : this.state.childrenShow;
183-
this.keysToEnterPaused = {};
184-
const emptyBool = !nextChildren.length && !currentChildren.length && this.state.children.length;
185-
/**
186-
* 在出场没结束时,childrenShow 里的值将不会清除。
187-
* 再触发进场时, childrenShow 里的值是保留着的, 设置了 forcedReplay 将重新播放进场。
188-
*/
189-
if (!emptyBool) {
190-
// 空子级状态下刷新不做处理
191-
const nextKeys = nextChildren.map(c => c.key);
192-
this.keysToLeave.forEach(key => {
193-
// 将所有在出场里的停止掉。避免间隔性出现
194-
if (nextKeys.indexOf(key) >= 0) {
195-
this.keysToEnterPaused[key] = true;
196-
currentChildren = currentChildren.filter(item => item.key !== key);
197-
if (nextProps.forcedReplay) {
198-
// 清掉所有出场的。
199-
delete childrenShow[key];
200-
}
201-
}
202-
});
203-
}
204-
205-
this.keysToEnter = [];
206-
this.keysToLeave = [];
207-
208-
// need render to avoid update
209-
this.setState({
210-
childrenShow,
211-
children: newChildren,
212-
});
213-
214-
nextChildren.forEach(c => {
215-
if (!c) {
216-
return;
217-
}
218-
const key = c.key;
219-
const hasPrev = findChildInChildrenByKey(currentChildren, key);
220-
if (!hasPrev && key) {
221-
this.keysToEnter.push(key);
222-
}
223-
});
224-
225-
currentChildren.forEach(c => {
226-
if (!c) {
227-
return;
228-
}
229-
const key = c.key;
230-
const hasNext = findChildInChildrenByKey(nextChildren, key);
231-
if (!hasNext && key) {
232-
this.keysToLeave.push(key);
233-
ticker.clear(this.placeholderTimeoutIds[key]);
234-
delete this.placeholderTimeoutIds[key];
235-
}
236-
});
237-
this.keysToEnterToCallback = [...this.keysToEnter];
238-
}
239-
240250
componentDidUpdate() {
241251
this.originalChildren = toArrayChildren(getChildrenFromProps(this.props));
242252
const keysToEnter = [...this.keysToEnter];
@@ -550,4 +560,4 @@ class QueueAnim extends React.Component {
550560
}
551561
}
552562
QueueAnim.isQueueAnim = true;
553-
export default QueueAnim;
563+
export default polyfill(QueueAnim);

0 commit comments

Comments
 (0)