React Native: состояние иногда равно нулю - setState работает асинхронно - PullRequest
0 голосов
/ 22 января 2019

Я думаю, у меня есть некоторые проблемы с состоянием моего приложения.Я уже понял, что this.setState({ ... }) - это функция, которая работает асинхронно. Итак, я думаю, что это как-то связано с моей проблемой.

Моя проблема в том, что я хочу показать всплывающее диалоговое окно моему пользователю, когда я отправляю push-уведомление через OneSignal.Это push-уведомление получает iOS и Android.Даже когда приложение работает в фоновом режиме, на переднем плане или было убито и даже не работает в фоновом режиме.Для всплывающего диалога я использую этот пакет: act-native-popup-dialog

Это всплывающее окно отображается только в том случае, если я отправляю определенные пары ключ / значение с push-уведомлением.Это следующие клавиши:

  1. showPopup: true - Отображение всплывающего окна при значении true.Если он не установлен или не равен true, он не отображается!
  2. openLink: mydomain.de - добавляет кнопку со ссылкой на всплывающее окно
  3. buttonText:Open in Browser - Устанавливает текст кнопки для ссылки

Обратите внимание, что дополнительная кнопка URL добавляется только во всплывающее окно, если установлены ключи openLink и buttonText.Ни одна из них или только одна из клавиш не установлены, эта кнопка не отображается.

Однако всплывающее диалоговое окно иногда появляется только в некоторых случаях.Я перечислю их для вас ниже:

  1. Случай 1: Приложение открыто.В этом случае всплывающее окно появляется на iOS и Android.Это обрабатывается функцией onReceived!
  2. Случай 2: Приложение полностью закрыто (убрано с экрана / убито).В этом случае всплывающее окно отображается на устройствах Android, но не на устройствах iOS!Это обрабатывается функцией onOpened!
  3. Случай 3: приложение было открыто и теперь работает в фоновом режиме.В этом случае всплывающее окно отображается на устройствах iOS, но не на устройствах Android.Это также обрабатывается функцией onOpened!

Итак, поскольку я не получаю сообщения об ошибках или что-то еще, я предполагаю, что я прав, полагая, что эта проблема вызвана асинхронностью this.setState({ ... }) функция.

Теперь у меня вопрос: как я могу убедиться, что состояние notification и visible всегда устанавливается перед рендерингом метода getPopup(...)? Я уже думал о его реализации, чтобы вызватьgetPopup(...) функция с параметрами.Итак, я могу быть уверен, что параметры всегда устанавливаются перед вызовом метода.Однако, к сожалению, это невозможно.Потому что класс, который вы видите ниже, класс SuperScreen, это просто класс, который расширяется некоторыми подклассами для связывания моего кода, такого как код push-уведомления или некоторые функции, которые мне нужны в каждом из этих подклассов.

ТакжеЯ уже пытался добавить переменную в мое состояние SuperClass, например, с именем stateSet, которая устанавливается после завершения функции setState({ ... }) onReceived или onOpened и проверить ее с помощью 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>
      );
    }
  }

Ответы [ 4 ]

0 голосов
/ 23 января 2019

Так что я думал об этой проблеме. Не могли бы вы решить это с помощью переменных?

Итак, в вашем конструкторе определите переменные, которые вам нужны.

constructor(props) {
  super(props);
  this.visible = false;
  this.pushNotification = null;

  this.state = {
    modalVisible: false,
  };
}

При получении push-уведомления openResult

this.pushNotification = openResult.notification
this.visible = true;

и onReceived 1011 *

this.pushNotification = notification
this.visible = true;

Вы по-прежнему хотите установить значение в состоянии, так как вы хотите запустить повторную визуализацию.

Тогда в вашем getPopUp переходе от использования этого this.state.pushNotification используйте this.pushNotification и аналогично для this.state.visible используйте this.visible

Значит, у вас Dialog должно быть что-то вроде

<Dialog
  visible={this.visible}
  ...

Затем в вашей кнопке для закрытия Dialog убедитесь, что вы звоните setState, но также обновите this.visible до false

Я создал очень простой пример того, о чем я говорю, https://snack.expo.io/SJVvK3BQN,, который использует Модал.

0 голосов
/ 22 января 2019

Используйте 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
      });
    }
  };
0 голосов
/ 22 января 2019

Вы можете использовать обратный вызов в setState !, я узнал об этом месяц назад, и с тех пор это было полезно. Отметьте в этой статье , вы можете передать функцию в качестве обратного вызова;)

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

Проверьте статью, она короткая и поможет

0 голосов
/ 22 января 2019

Вы можете добавить обратный вызов в setState, чтобы гарантировать выполнение кода после установки состояния:

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