cooperka / react-native-snackbar

:candy: Material Design "Snackbar" component for Android and iOS.
Other
823 stars 152 forks source link

Snackbar.show is working only sometimes #132

Closed gvsakhil closed 4 years ago

gvsakhil commented 4 years ago

I am trying to show some toasters based on api response and if I add a snackbar before try block its working fine and the snackbar that are inisde try, catch blocks are not being executed. I try settimeout but it didn't work for me.

package.json

"react": "16.9.0", "react-native": "0.61.5", "react-native-snackbar": "^2.0.3",

My Code

async function sendEmail() { Snackbar.show({ title: 'Reset link sent successfully', duration: Snackbar.LENGTH_INDEFINITE, }); setLoading(true); try { const res = await axios.post(${Constants.apiUrl}auth/requestresetpassword/${email}/WITTYPARROT, {}); if (res.status == 202) { Snackbar.show({ title: 'Reset link sent successfully', duration: Snackbar.LENGTH_INDEFINITE, }); } setLoading(false); } catch (e) { console.log(e.response.status); if (e.response.status == 400) { Snackbar.show({ title: 'Please enter a valid email address', duration: Snackbar.LENGTH_INDEFINITE, }, 1000); } else { Snackbar.show({ title: 'Something went wrong', duration: Snackbar.LENGTH_SHORT, }, 1000); } setLoading(false); } }

cooperka commented 4 years ago

Strange! In order to help debug, do you mind creating a working example app I can download and run? That would help tremendously. Cheers.

GunnarAK commented 4 years ago

I'm having this issue on Android only. iOS works as expected.. I've got a component called ScreenContainer which checks network availability:

class ScreenContainer extends Component<Props, State> {

  componentDidUpdate(prevProps: Props) {
    this.checkNetworkAvailability(prevProps);
  }

  checkNetworkAvailability(prevProps?: Props) {
    if (
      prevProps &&
      !prevProps.networkAvailable &&
      this.props.networkAvailable
    ) {
      console.warn("Snackbar dismiss");
      // Dismiss ANY existing Snackbars: https://github.com/cooperka/react-native-snackbar#snackbardismiss
      Snackbar.dismiss();
    } else if (!this.props.networkAvailable) {
      console.warn("Snackbar show, networkAvailable:", this.props.networkAvailable);
      try {
        Snackbar.show({
          text: "No network",
          duration: Snackbar.LENGTH_INDEFINITE,
          action: __DEV__ && {
            text: "(dev) UNDO",
            textColor: "green",
            onPress: () => {
              /* Do something. */
            }
          }
        });
      } catch (error) {
        console.error("showing snackbar failed");
      }
    }
  }
  render() {
    return (
      <SafeAreaView style={{ flex: 1 }}>
         ...
      </SafeAreaView>
    );
  }
}
const mapStateToProps = (state: GlobalState): MapStateToProps => {
  return {
    networkAvailable: state.api.networkAvailable
  };
};
export default connect(mapStateToProps)(ScreenContainer);

I use this in other class components:

render() {
    return (
      <ScreenContainer>
       ...
      </ScreenContainer>
    );
  }

I'm seeing all the console warnings as expected, but the Snackbar is not showing up. The Snackbar does show up when a class component using ScreenContainer is mounted (componentDidMount). Then toggling airplane mode dismisses the Snackbar but it will not reappear again.. While console warnings show Snackbar show, networkAvailable: false

The console.error is never thrown.

npx react-native info:

System:
    OS: macOS 10.15.5
    CPU: xxxxx
    Memory: xxxxx
    Shell: xxxxx
  Binaries:
    Node: 13.7.0 - /usr/local/bin/node
    npm: 6.14.5 - /usr/local/bin/npm
    Watchman: 4.9.0 - /usr/local/bin/watchman
  SDKs:
    iOS SDK:
      Platforms: iOS 13.4, DriverKit 19.0, macOS 10.15, tvOS 13.4, watchOS 6.2
    Android SDK:
      API Levels: 28, 29
      Build Tools: 28.0.3, 29.0.3, 30.0.0, 30.0.0
      System Images: android-29 | Google Play Intel x86 Atom
  IDEs:
    Android Studio: 3.6 AI-192.7142.36.36.6392135
    Xcode: 11.4.1/11E503a - /usr/bin/xcodebuild
  npmPackages:
    @react-native-community/cli: ^4.0.1 => 4.10.0 
    react: 16.9.0 => 16.9.0 
    react-native: ~0.61.5 => 0.61.5 
  npmGlobalPackages:
    react-native-ble-plx: 1.0.3
    react-native-create-library: 3.1.2
    react-native-git-upgrade: 0.2.7
    react-native-permissions: 1.1.1

EDIT: added react-native info

GunnarAK commented 4 years ago

I found what I was doing wrong. For turning off/on network capabilities I swiped down my QuickSettings pane. By doing this the application window loses focus. There are numerous cases where this is unexpected behaviour and you fail to deliver your message to the user. I put breakpoints at line 66 and 68 in SnackbarModule.java, they were triggered when QuickSettings was visible. But 71: displaySnackbar(modal, options, callback); was not..

Note to self: Toggle network by adb:

adb shell svc wifi disable
-- and --
adb shell svc wifi enable

I did managed to get around this issue by altering SnackbarModule.java. I'll see if I can submit a PR.

hasWindowFocus(): https://developer.android.com/reference/android/app/Activity#hasWindowFocus()

getVisibility(): https://developer.android.com/reference/android/view/View#getVisibility()

Old

if (!view.hasWindowFocus()) {
    // The view is not focused, we should get all the modal views in the screen.
    ArrayList<View> modals = recursiveLoopChildren(view, new ArrayList<View>());

    for (View modal : modals) {
        if (modal == null) continue;

        displaySnackbar(modal, options, callback);
    }

    return;
}

New

if (!view.hasWindowFocus()) {
    // The view is not focused, we should get all the modal views in the screen.
    ArrayList<View> modals = recursiveLoopChildren(view, new ArrayList<View>());

    if (modals.size() > 0) {
        for (View modal : modals) {
            if (modal == null) continue;

            displaySnackbar(modal, options, callback);
        }
    } else if (view.getVisibility() == View.VISIBLE) {
        displaySnackbar(view, options, callback);
    }
    return;
}