1

我正在尝试创建一个类似于 Snackbar 的状态消息组件,其签名如下所示:

<StatusMsg
 open={this.state.isOpen}
 message={this.state.msg}
 onRequestClose={someFunc}
 />

当“open”设置为 true 时,消息应该在屏幕上显示动画,在屏幕上停留一段时间,然后再次关闭。

因此,下面的代码可以正常工作,除了在一个相当常见的用例中:当调用组件在旧消息动画关闭屏幕之前设置新消息时。在这种情况下,下面的代码只是替换当前显示的消息并使用相同的计时器。

理想情况下,当添加新消息时,应该首先使旧消息在屏幕外显示动画,然后在新消息中显示动画。(即使新消息文本与旧消息文本相同,我也想确保它这样做。)

我似乎无法弄清楚如何做这样的事情。我最好的猜测是 StatusMsg 实际上应该是一个工厂,用于创建包含消息的动画视图,但我不知道如何使其工作。具体来说,如何使先前的消息在创建新消息时自行关闭。

class StatusMsg extends Component {
  state = { exited: false, moveHeightAnimated: new Animated.Value(HIDDEN_HEIGHT) }
  timerAutoHide = null;

  componentWillMount() {
    if (!this.props.open) {
      this.setState({ exited: true });
    }
  }

  componentDidMount() {
    if (this.props.open) {
      this.show();
    }
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.open && this.state.exited) {
      this.setState({ exited: false });
    }
  }

  componentDidUpdate(prevProps) {
    if (prevProps.open !== this.props.open) {
      if (this.props.open) {
        this.show();
      } else {
        clearTimeout(this.timerAutoHide);
      }
    }
  }

  componentWillUnmount() {
    clearTimeout(this.timerAutoHide);
  }

  show() {
    const toValue = SHOW_HEIGHT;
    Animated.timing(this.state.moveHeightAnimated, {
        toValue,
        duration: 225,
        easing: Easing.bezier(0.0, 0.0, 0.2, 1),
    }).start(this.setAutoHideTimer);
  }

  hide() {
    const toValue = HIDDEN_HEIGHT;
    Animated.timing(this.state.moveHeightAnimated, {
        toValue,
        duration: 195,
        easing: Easing.bezier(0.4, 0.0, 1, 1),
    }).start(this.onFinishedHiding);
  }

  onFinishedHiding = () => {
    this.setState({ exited: true });
    if (this.props.onRequestClose) {
      this.props.onRequestClose();
    }
  }

  setAutoHideTimer = () => {
    if (!this.props.onRequestClose) {
      return;
    }

    clearTimeout(this.timerAutoHide);
    this.timerAutoHide = setTimeout(() => {
      if (!this.props.onRequestClose) {
        return;
      }

      this.hide();
    }, this.props.autoHideDuration || DEFAULT_AUTOHIDE_DURATION);
  }

  render() {
    if (!this.props.open && this.state.exited) {
      return null;
    }

    return (
      <Animated.View style={[styles.msgContainerStyle, { bottom: this.state.moveHeightAnimated }]} >
        <View style={styles.noticeContainerStyle}>
          <Text style={styles.msgTextStyle}>{this.props.message}</Text>
        </View>
      </Animated.View>
    );
  }
}
4

0 回答 0