在一个基于 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.

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐