reactjs - 在 React Native 中订阅监听器的通用解决方案

标签 reactjs react-native

是否有一种可重用的方式来订阅诸如键盘事件之类的监听器。

实际上,我在屏幕的最底部有一个绝对位置的按钮,当键盘弹出时,它会 float 在顶部,看起来不太好。

因此,当键盘可见时,我会隐藏该按钮,但如果您在多个屏幕上有类似的情况,那么在每个屏幕上添加订阅就会变得令人头疼,目前我正在这样做。

class Profile extends Component {
  constructor(props) {
    super(props);

    this._keyboardDidShow = this._keyboardDidShow.bind(this);
    this._keyboardDidHide = this._keyboardDidHide.bind(this);
  }

  componentDidMount() {
    // subscribing to keyboard listeners on didMount

    this.keyboardDidShowListener = Keyboard.addListener(
      'keyboardDidShow',
      this._keyboardDidShow
    );

    this.keyboardDidHideListener = Keyboard.addListener(
      'keyboardDidHide',
      this._keyboardDidHide
    );
  }

  _keyboardDidShow() {
    this.setState({
      keyboardVisible: true,
    });
  }

  _keyboardDidHide() {
    this.setState({
      keyboardVisible: false,
    });
  }

  componentWillUnmount() {
    // unsubscribing listeners on unMount

    this.keyboardDidShowListener.remove();
    this.keyboardDidHideListener.remove();
  }

  render() {
    const AnimatedBottomButton = Animated.createAnimatedComponent(BottomButton);

    return (
      <ScrollView
        style={styles.containerStyle}
        bounces={false}
        contentContainerStyle={{ flex: 1 }}
        keyboardShouldPersistTaps="handled">
        {this.renderUserImage()}
        {this.renderUserDetail()}
        {!this.state.keyboardVisible && (
          <View
            style={{
              flex: 1,
              justifyContent: 'flex-end',
            }}>
            <AnimatedBottomButton
              title="Done"
              onPress={() => Actions.pop()}
              style={{
                opacity: this.anim5,
                transform: [{ scale: this.anim5 }],
                marginBottom: Utils.isPhoneX() ? Metrics.doubleBaseMargin : 0,
              }}
            />
          </View>
        )}
      </ScrollView>
    );
  }
}

我不喜欢上面的解决方案,因为我必须向我想要订阅键盘事件的每个组件添加订阅相关的代码,我是 JavaScript 新手,仍在学习它。

如果有人可以帮助我找到一些通用的解决方案,那就太好了。

最佳答案

自定义组件在这些情况下会派上用场。您可以创建一个实现所需行为的组件,然后将该组件添加到您要使用的屏幕中。

示例

export default class CustomButton extends Component {
  state = {
    visible: true
  }
  componentDidMount() {
    // subscribing to keyboard listeners on didMount
    this.keyboardDidShowListener = Keyboard.addListener(
      'keyboardDidShow',
      () => this._toggleVisiblity(false)
    );

    this.keyboardDidHideListener = Keyboard.addListener(
      'keyboardDidHide',
      () => this._toggleVisiblity(true)
    );
  }
  _toggleVisiblity = (visible) => {
    this.setState({ visible })
  }
  componentWillUnmount() {
    // unsubscribing listeners on unMount

    this.keyboardDidShowListener.remove();
    this.keyboardDidHideListener.remove();
  }
  render() {
    if (this.state.visible === false) return null
    return (
      <View
        style={{
          flex: 1,
          justifyContent: 'flex-end',
        }}>
        <AnimatedBottomButton
          title="Done"
          onPress={() => Actions.pop()}
          style={{
            opacity: this.anim5,
            transform: [{ scale: this.anim5 }],
            marginBottom: Utils.isPhoneX() ? Metrics.doubleBaseMargin : 0,
          }}
        />
      </View>
    );
  }
}

class Profile extends Component {
  render() {
    return (
      <ScrollView
        style={styles.containerStyle}
        bounces={false}
        contentContainerStyle={{ flex: 1 }}
        keyboardShouldPersistTaps="handled">
        {this.renderUserImage()}
        {this.renderUserDetail()}
        <CustomButton />
      </ScrollView>
    );
  }
}

如果您愿意,您可以更进一步,创建 HOC

示例

const withKeyboardEvents = WrappedComponent => {
  return class extends Component {
    state = {
      visible: true,
    };
    componentDidMount() {
      this.keyboardDidShowListener = Keyboard.addListener(
        'keyboardDidShow',
        () => this._toggleVisiblity(false)
      );
      this.keyboardDidHideListener = Keyboard.addListener(
        'keyboardDidHide',
        () => this._toggleVisiblity(true)
      );
    }
    _toggleVisiblity = visible => {
      this.setState({ visible });
    };
    componentWillUnmount() {
      this.keyboardDidShowListener.remove();
      this.keyboardDidHideListener.remove();
    }
    render() {
      return (
        <React.Fragment>
          {this.state.visible === true && (
            <View
              style={{
                flex: 1,
                justifyContent: 'flex-end',
              }}>
              <AnimatedBottomButton
                title="Done"
                onPress={() => Actions.pop()}
                style={{
                  opacity: this.anim5,
                  transform: [{ scale: this.anim5 }],
                  marginBottom: Utils.isPhoneX() ? Metrics.doubleBaseMargin : 0,
                }}
              />
            </View>
          )}
          <WrappedComponent />
        </React.Fragment>
      );
    }
  };
};

class Profile extends Component {
  render() {
    return (
      <ScrollView
        style={styles.containerStyle}
        bounces={false}
        contentContainerStyle={{ flex: 1 }}
        keyboardShouldPersistTaps="handled">
        {this.renderUserImage()}
        {this.renderUserDetail()}
      </ScrollView>
    );
  }
}
export default withKeyboardEvents(Profile)

关于reactjs - 在 React Native 中订阅监听器的通用解决方案,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51056989/

相关文章:

javascript - 使用mapStateToProps时的React和Redux : why isn't my value not displaying from reducer,

javascript - 如何在 reactjs new hook api 中进行 api 调用并跨组件共享?

javascript - 如何在 react.js 中过滤多个数组上的对象列表?

javascript - 透明 NavigatorIOS 不起作用

android - 如何在 React Native 中提交文件?

javascript - React.js JSON 数据更新,但状态不更新,因此 UI 不更新

mysql - 通过Mysql React原生json解析

android - React Native Android WebView 如何禁用水平滚动?

react-native - 如何在 native react 中添加文本输入以发出警报

reactjs - store.dispatch 无法读取未定义的属性 'then'