【问题标题】:React Native: state is null sometimes - setState is working asynchronouslyReact Native:状态有时为空 - setState 正在异步工作
【发布时间】:2019-01-22 13:56:20
【问题描述】:

我认为我的应用程序状态确实存在一些问题。我已经发现this.setState({ ... }) 是一个异步工作的函数。所以,我认为这与我的问题有关。

我的问题是,当我通过 OneSignal 发送推送通知时,我想向我的用户显示一个对话框弹出窗口。 iOS 和 Android 都会收到此推送通知。即使应用程序在 背景,前景或被杀死,甚至没有在后台运行。对于弹出对话框,我正在使用这个包:react-native-popup-dialog

仅当我通过推送通知发送某些键/值对时,此弹出窗口才可见。这些键是:

  1. showPopup:true - 为真时显示弹出窗口。如果未设置或不等于 true,则不显示!
  2. openLink:mydomain.de- 添加带有弹出窗口链接的按钮
  3. buttonText:Open in Browser - 将按钮文本设置为链接

注意,额外的 URL 按钮仅在设置了键 openLinkbuttonText 时才会添加到弹出窗口中。其中没有一个或只设置了一个键,它不显示此按钮。

但是,弹出对话框有时仅在某些情况下显示。我将在下面为您列出:

  1. 案例 1:应用程序已打开。在这种情况下,弹出窗口会出现在 iOS 和 Android 上。这由 onReceived函数!
  2. 案例 2:应用程序完全关闭(从屏幕上滑出/被杀死)。在这种情况下,弹出窗口显示在 Android 上 设备,但不在 iOS 设备上!这由onOpened 函数处理!
  3. 案例 3:应用程序已打开,现在正在后台运行。在这种情况下,弹出窗口会显示在 iOS 上 设备,但不适用于 Android 设备。这也由onOpened 函数处理!

所以,由于我没有收到错误消息或其他信息,我想我的猜测是正确的,即这个问题是 由于异步 this.setState({ ... }) 函数。

我现在的问题是如何确保在呈现getPopup(...) 方法之前始终设置notificationvisible 的状态。我已经在考虑实现它,所以我调用getPopup(...)带参数的函数。因此,我可以确定在调用方法之前始终设置参数。然而,遗憾的是这是不可能的。因为你在下面看到的类,SuperScreen 类,只是一个由一些子类扩展的类,以捆绑我的代码,如推送通知代码或我在每个子类中需要的一些函数。

另外,我已经尝试将变量添加到我的 SuperClass 状态,例如称为stateSet,在onReceivedonOpenedsetState({ ... }) 函数完成后设置并使用 if(this.state.stateSet)getPopup(...) 函数的第一行。然而,这也是不可能的。原因是当我按下Ok 或链接按钮时,我的弹出窗口不再关闭。

如果你们对如何解决这个问题有任何想法,我将不胜感激! 这是我的代码:

export default class SuperScreen extends Component {
  constructor(props) {
    super(props);
    this.state = {
      pushNotification: null,
      visible: false
    };

    OneSignal.init("00000000", {
      kOSSettingsKeyAutoPrompt: true
    });

    OneSignal.inFocusDisplaying(0);
    OneSignal.enableVibrate(true);
    OneSignal.enableSound(true);

    OneSignal.addEventListener("received", this.onReceived);
    OneSignal.addEventListener("opened", this.onOpened);
    OneSignal.addEventListener("ids", this.onIds);
  }

  componentWillUnmount() {
    OneSignal.removeEventListener("received", this.onReceived);
    OneSignal.removeEventListener("opened", this.onOpened);
    OneSignal.removeEventListener("ids", this.onIds);
  }

  onReceived = notification => {
    //App is opened!
    console.log("Notification received: ", notification);

    this.setState({
      pushNotification: notification,
      visible: true
    });

    if (notification.payload.notificationID != null) {
      firebase.analytics().logEvent("Popup_Link_Button", {
        notificationID: notification.payload.notificationID,
        clicked: true
      });
    }
  };

  onOpened = openResult => {
    //App either is closed or running in background
    //Android:  Closed: Showing       Background: Not Showing
    //iOS:      Closed: Not Showing   Background: Showing)

    console.log("openResult: ", openResult);

    this.setState({
      pushNotification: openResult.notification,
      visible: true
    });

    if (openResult.notification.payload.notificationID != null) {
      firebase.analytics().logEvent("Popup_Link_Button", {
        notificationID: openResult.notification.payload.notificationID,
        clicked: true
      });
    }
  };

  onIds = device => {
    console.log("Device info: ", device);
  };

  getPopup() {
    if (
      this.state.pushNotification != null &&
      this.state.pushNotification.payload.additionalData != null &&
      this.state.pushNotification.payload.additionalData.showPopup != null &&
      this.state.pushNotification.payload.additionalData.showPopup == "true"
    ) {
      var actionButtons = null;

      if (
        this.state.pushNotification.payload.additionalData.openLink != null &&
        this.state.pushNotification.payload.additionalData.buttonText != null
      ) {
        actionButtons = [
          <DialogButton
            text="Ok"
            key={0}
            onPress={() => {
              this.setState({ visible: false });
              firebase.analytics().logEvent("Popup_Link_Button", {
                notificationID: this.state.pushNotification.payload
                  .notificationID,
                opened: false
              });
            }}
          />
        ];

        actionButtons.push(
          <DialogButton
            text={this.state.pushNotification.payload.additionalData.buttonText}
            key={1}
            onPress={() => {
              this.openLink(
                this.state.pushNotification.payload.additionalData.openLink
              );
              this.setState({ visible: false });
              firebase.analytics().logEvent("Popup_Link_Button", {
                notificationID: this.state.pushNotification.payload
                  .notificationID,
                link: this.state.pushNotification.payload.additionalData
                  .openLink,
                opened: true
              });
            }}
          />
        );
      } else {
        actionButtons = [
          <DialogButton
            text="Ok"
            key={0}
            onPress={() => {
              this.setState({ visible: false });
              firebase.analytics().logEvent("Popup_Link_Button", {
                popupID: this.state.pushNotification.payload.notificationID,
                opened: false
              });
            }}
          />
        ];
      }

      return (
        <Dialog
          visible={this.state.visible}
          dialogTitle={
            <DialogTitle
              title={
                this.state.pushNotification == null
                  ? ""
                  : this.state.pushNotification.payload.title
              }
            />
          }
          dialogAnimation={
            new SlideAnimation({
              slideFrom: "bottom"
            })
          }
          dialogStyle={{ marginLeft: 20, marginRight: 20 }}
          actions={actionButtons}
        >
          <DialogContent>
            <Text />
            <Text>
              {this.state.pushNotification == null
                ? ""
                : this.state.pushNotification.payload.body}
            </Text>
          </DialogContent>
        </Dialog>
      );
    }
  }

【问题讨论】:

  • 发布的代码足够庞大,但缺少 stackoverflow.com/help/mcve 。没有你提到的 stateSet 。不使用 getPopup。考虑提供一种方法来复制问题。像 Stackblitz 这样可以调试的演示会有所帮助。如果问题是异步状态,那么这种情况可能并不特定于 Native,因此组件可能会被存根。
  • 我的问题或多或少无法复制,因为您无法在 Stackoverflow 上收到推送通知

标签: reactjs react-native null state setstate


【解决方案1】:

你可以给 setState 添加一个回调来确保设置状态后代码运行:

this.setState().then({ //Do something here. })

【讨论】:

    【解决方案2】:

    异步使用async, await

    onReceived = async (notification) => {
        //App is opened!
        console.log("Notification received: ", notification);
    
        await this.setState({ // It will wait until finish setState. 
          pushNotification: notification,
          visible: true
        }); 
    
        if (notification.payload.notificationID != null) {
          firebase.analytics().logEvent("Popup_Link_Button", {
            notificationID: notification.payload.notificationID,
            clicked: true
          });
        }
      };
    

    【讨论】:

    • 当我让这些功能异步工作时,我的推送通知不会收到......!
    【解决方案3】:

    您可以在 setState! 中使用回调,我在一个月前学习了它,并且从 .检查this article,您可以将函数作为回调传递;)

    this.setState(
        { pushNotification: notification,
          visible: true }, () => {this.getPopup()}) //this.getPopup it's the second parameter, a callback
    

    查看这篇文章,它很短,会有所帮助

    【讨论】:

    • 但问题是,如前所述,函数getPopup(...) get 仅从子类中调用..
    • 一种解决方法是告诉子类再次触发渲染方法,例如,子类调用getPopup(...) 方法..但我不知道这是否可能..跨度>
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-01
    • 2021-07-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-12
    相关资源
    最近更新 更多