React Native 弹框组件“失灵” ,一次动画并发问题的排查与修复
·
在一个基于 Animated 实现的自定义弹框组件 AppActionSheet 中,业务页面触发弹框显示时,出现弹框“出不来”的情况 —— 调用方已将 isShow 设为 true,但 UI 上没有任何反应,且无报错。
首先检查 componentDidUpdate 中的判断逻辑
componentDidUpdate(prevProps) {
if (this.props.isShow && !prevProps.isShow) {
this.registerBackHandler();
this.show();
} else if (!this.props.isShow && prevProps.isShow) {
this.hide();
}
}
逻辑本身没有问题:isShow 从 false → true 调用 show(),反之调用 hide()。问题不在这个入口。
查看核心实现:
show = () => {
this.setState({ visible: true });
Animated.timing(this.slideAnim, {
toValue: 0,
duration: 200,
useNativeDriver: true,
}).start();
};
hide = () => {
Animated.timing(this.slideAnim, {
toValue: screenHeight,
duration: 200,
useNativeDriver: true,
}).start(() => {
this.setState({ visible: false });
this.removeBackHandler();
});
};
hide() 启动了一个 200ms 的滑出动画,并在动画完成回调中将 visible 置为 false。如果在这 200ms 内再次调用 show(),show() 会执行 setState({ visible: true }),但旧的 hide 动画并未被取消。旧动画执行完毕后,其回调会将 visible 再次设为 false,覆盖掉刚刚设置的 true。
方案:在启动新动画前,停止所有正在运行的动画。引入一个标志变量,在动画回调中判断当前“期望”的可见状态,避免过期回调误操作。组件卸载时清理,防止内存泄漏。
最终实现:
export class AppActionSheet extends React.Component {
constructor(props) {
super(props);
this.state = { visible: false };
this.slideAnim = new Animated.Value(screenHeight);
this.animation = null; // 保存当前动画实例
this.isVisibleExpected = false; // 期望状态标志
}
show = () => {
this.isVisibleExpected = true;
// 取消正在运行的动画
if (this.animation) {
this.animation.stop();
this.animation = null;
}
this.setState({ visible: true });
this.animation = Animated.timing(this.slideAnim, {
toValue: 0,
duration: 200,
useNativeDriver: true,
});
this.animation.start(() => {
this.animation = null;
});
};
hide = () => {
this.isVisibleExpected = false;
if (this.animation) {
this.animation.stop();
this.animation = null;
}
this.animation = Animated.timing(this.slideAnim, {
toValue: screenHeight,
duration: 200,
useNativeDriver: true,
});
this.animation.start(() => {
// 只有期望为 false 时才真正隐藏
if (!this.isVisibleExpected) {
this.setState({ visible: false });
this.removeBackHandler();
}
this.animation = null;
});
};
componentWillUnmount() {
// 组件卸载时停止动画
if (this.animation) {
this.animation.stop();
this.animation = null;
}
this.removeBackHandler();
}
}
ok.
更多推荐


所有评论(0)