Skip to content

RTK Query: refetchOnFocus is not working for me in a ReactNative app #3652

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
jr00n opened this issue Aug 11, 2023 · 15 comments
Closed

RTK Query: refetchOnFocus is not working for me in a ReactNative app #3652

jr00n opened this issue Aug 11, 2023 · 15 comments
Labels
docs React-Native Indicates an issue specific to the React Native environment RTK-Query

Comments

@jr00n
Copy link

jr00n commented Aug 11, 2023

Hi, reading the docs at https://redux-toolkit.js.org/rtk-query/usage/cache-behavior#re-fetching-on-window-focus-with-refetchonfocus
I expect my queries to be refetched when the app is gaining focus again when running in the background for a while...but nothing happens.

export const apiSlice = createApi({
  baseQuery: baseQueryWithReauth,
  endpoints: () => ({
  ...
  }),
  refetchOnFocus: true,
});

What do I miss?

@jr00n jr00n changed the title refetchOnFocus is not working for me in a ReactNative app RTK Query: refetchOnFocus is not working for me in a ReactNative app Aug 11, 2023
@Ahmedhamed77
Copy link

Ahmedhamed77 commented Aug 15, 2023

the same issue even if I added setupListeners(store.dispatch) it will not work too
@phryneas does react-native requires an additional step to make refetchOnFocus and refetchOnReconnect work?

@shivam9aug1996
Copy link

On web it works but on mobile its not working

@markerikson
Copy link
Collaborator

The current implementation is:

      if (typeof window !== 'undefined' && window.addEventListener) {
        // Handle focus events
        window.addEventListener(
          'visibilitychange',
          handleVisibilityChange,
          false
        )
        window.addEventListener('focus', handleFocus, false)

        // Handle connection events
        window.addEventListener('online', handleOnline, false)
        window.addEventListener('offline', handleOffline, false)
        initialized = true
      }

I genuinely have no idea if that even runs on React Native. Can anyone check and confirm if those events exist in RN?

@phryneas
Copy link
Member

Yes, the default implementation of setupListeners is for web.

See #1931

We had a PR for documentation for this, but got stuck on the final implementation to put into the docs.

@mikemonaco
Copy link

It would be great if this was supported in React Native.

@markerikson
Copy link
Collaborator

@mikemonaco afraid we're not React Native users, and I have no idea how focus gets handled in RN. We'd be open to a PR that improves this, but I don't expect us to try to work on that ourselves.

@phryneas
Copy link
Member

@mikemonaco The comment directly above your comment links to a step-by-step solution on how to get this working in React Native.

@WhidRubeld
Copy link

WhidRubeld commented Jan 18, 2024

My working and tested code to set up listeners in React Native project

import NetInfo, {
  NetInfoState,
  NetInfoSubscription,
} from '@react-native-community/netinfo'
import { ThunkDispatch } from '@reduxjs/toolkit'
import { AppState, AppStateStatus, NativeEventSubscription } from 'react-native'

let initialized = false

let appStateSubscription: NativeEventSubscription | null = null
let appState = AppState.currentState

let netInfoUnsubscribe: NetInfoSubscription | null = null

export function listenHandler(
  dispatch: ThunkDispatch<any, any, any>,
  {
    onFocus,
    onFocusLost,
    onOffline,
    onOnline,
  }: {
    onFocus: () => void
    onFocusLost: () => void
    onOffline: () => void
    onOnline: () => void
  }
) {
  const handleFocus = () => dispatch(onFocus())
  const handleFocusLost = () => dispatch(onFocusLost())
  const handleOnline = () => dispatch(onOnline())
  const handleOffline = () => dispatch(onOffline())

  const _handleAppStateChange = (nextAppState: AppStateStatus) => {
    const foreground = !!(
      appState.match(/inactive|background/) && nextAppState === 'active'
    )

    if (foreground) handleFocus()
    else handleFocusLost()

    appState = nextAppState
  }

  const _handleNetInfoChange = (state: NetInfoState) => {
    const { isConnected } = state

    if (isConnected) handleOnline()
    else handleOffline()
  }

  if (!initialized) {
    appStateSubscription = AppState.addEventListener(
      'change',
      _handleAppStateChange
    )
    netInfoUnsubscribe = NetInfo.addEventListener(_handleNetInfoChange)
    initialized = true
  }

  const unsubscribe = () => {
    if (appStateSubscription) {
      appStateSubscription.remove()
      appStateSubscription = null
    }
    if (netInfoUnsubscribe) {
      netInfoUnsubscribe()
      netInfoUnsubscribe = null
    }
    initialized = false
  }

  return unsubscribe
}

@anaselbahrawy
Copy link

it's work with

setupListeners(store.dispatch)

@sleekLancelot
Copy link

it's work with

setupListeners(store.dispatch)

On react native it doesn't

@sleekLancelot
Copy link

My working and tested code to set up listeners in React Native project

import NetInfo, {
NetInfoState,
NetInfoSubscription,
} from '@react-native-community/netinfo'
import { ThunkDispatch } from '@reduxjs/toolkit'
import { AppState, AppStateStatus, NativeEventSubscription } from 'react-native'

let initialized = false

let appStateSubscription: NativeEventSubscription | null = null
let appState = AppState.currentState

let netInfoUnsubscribe: NetInfoSubscription | null = null

export function listenHandler(
dispatch: ThunkDispatch<any, any, any>,
{
onFocus,
onFocusLost,
onOffline,
onOnline,
}: {
onFocus: () => void
onFocusLost: () => void
onOffline: () => void
onOnline: () => void
}
) {
const handleFocus = () => dispatch(onFocus())
const handleFocusLost = () => dispatch(onFocusLost())
const handleOnline = () => dispatch(onOnline())
const handleOffline = () => dispatch(onOffline())

const _handleAppStateChange = (nextAppState: AppStateStatus) => {
const foreground = !!(
appState.match(/inactive|background/) && nextAppState === 'active'
)

if (foreground) handleFocus()
else handleFocusLost()

appState = nextAppState

}

const _handleNetInfoChange = (state: NetInfoState) => {
const { isConnected } = state

if (isConnected) handleOnline()
else handleOffline()

}

if (!initialized) {
appStateSubscription = AppState.addEventListener(
'change',
_handleAppStateChange
)
netInfoUnsubscribe = NetInfo.addEventListener(_handleNetInfoChange)
initialized = true
}

const unsubscribe = () => {
if (appStateSubscription) {
appStateSubscription.remove()
appStateSubscription = null
}
if (netInfoUnsubscribe) {
netInfoUnsubscribe()
netInfoUnsubscribe = null
}
initialized = false
}

return unsubscribe
}

Bless your heart, this worked!
Thanks.

@shivam9aug1996
Copy link

shivam9aug1996 commented Nov 26, 2024

import { ThunkDispatch, createAction } from "@reduxjs/toolkit";
import { addNetworkStateListener } from "expo-network";
import {
  Platform,
  AppState,
  AppStateStatus,
  NativeEventSubscription,
} from "react-native";
import { type EventSubscription } from "expo-modules-core";

export const onFocus = createAction("__rtkq/focused");
export const onFocusLost = createAction("__rtkq/unfocused");
export const onOnline = createAction("__rtkq/online");
export const onOffline = createAction("__rtkq/offline");

let initialized: Boolean = false;


export function setupListeners(
  dispatch: ThunkDispatch<any, any, any>,
  customHandler?: (
    dispatch: ThunkDispatch<any, any, any>,
    actions: {
      onFocus: typeof onFocus;
      onFocusLost: typeof onFocusLost;
      onOnline: typeof onOnline;
      onOffline: typeof onOffline;
    }
  ) => () => void
) {
  const mobileDefaultHandler = () => {
    const handleFocus = () => dispatch(onFocus());
    const handleFocusLost = () => dispatch(onFocusLost());
    const handleOnline = () => dispatch(onOnline());
    const handleOffline = () => dispatch(onOffline());

    let unsubscribeAppState: NativeEventSubscription;
    let networkSubscription: EventSubscription;

    if (!initialized) {
      const handleAppStateChange = (nextAppState: AppStateStatus) => {
        if (nextAppState === "active") {
          handleFocus();
        } else {
          handleFocusLost();
        }
      };

      unsubscribeAppState = AppState.addEventListener(
        "change",
        handleAppStateChange
      );

      networkSubscription = addNetworkStateListener(
        ({ type, isConnected, isInternetReachable }) => {
          if (isConnected && isInternetReachable) {
            handleOnline();
          } else {
            handleOffline();
          }
          console.log(
            `Network type: ${type}, Connected: ${isConnected}, Internet Reachable: ${isInternetReachable}`
          );
        }
      );

      initialized = true;
    }

    const unsubscribe = () => {
      if (unsubscribeAppState) unsubscribeAppState.remove();
      if (networkSubscription) networkSubscription.remove();
      initialized = false;
    };

    return unsubscribe;
  };

  const webDefaultHandler = () => {
    const handleFocus = () => dispatch(onFocus());
    const handleFocusLost = () => dispatch(onFocusLost());
    const handleOnline = () => dispatch(onOnline());
    const handleOffline = () => dispatch(onOffline());
    const handleVisibilityChange = () => {
      if (window.document.visibilityState === "visible") {
        handleFocus();
      } else {
        handleFocusLost();
      }
    };

    if (!initialized) {
      if (typeof window !== "undefined" && window.addEventListener) {
        // Handle focus events
        window.addEventListener(
          "visibilitychange",
          handleVisibilityChange,
          false
        );
        window.addEventListener("focus", handleFocus, false);

        // Handle connection events
        window.addEventListener("online", handleOnline, false);
        window.addEventListener("offline", handleOffline, false);
        initialized = true;
      }
    }
    const unsubscribe = () => {
      window.removeEventListener("focus", handleFocus);
      window.removeEventListener("visibilitychange", handleVisibilityChange);
      window.removeEventListener("online", handleOnline);
      window.removeEventListener("offline", handleOffline);
      initialized = false;
    };
    return unsubscribe;
  };

  return customHandler
    ? customHandler(dispatch, { onFocus, onFocusLost, onOffline, onOnline })
    : Platform.OS === "web"
    ? webDefaultHandler()
    : mobileDefaultHandler();
}

@aryaemami59 aryaemami59 added the React-Native Indicates an issue specific to the React Native environment label Jan 3, 2025
@wmonecke
Copy link

wmonecke commented Jan 8, 2025

@WhidRubeld @sleekLancelot where exactly would you set this code?

@WhidRubeld
Copy link

@sleekLancelot
Copy link

@WhidRubeld @sleekLancelot where exactly would you set this code?

In your store.ts/.js file

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs React-Native Indicates an issue specific to the React Native environment RTK-Query
Projects
None yet
Development

No branches or pull requests