redux-toolkit
redux-toolkit copied to clipboard
Add setupListeners example for react-native
I recently had to create this for myself when experimenting with rtk-query in a react-native project.
I figured this could be useful for somebody else.
This pull request is automatically built and testable in CodeSandbox.
To see build info of the built libraries, click here or the icon next to each commit SHA.
Latest deployment of this branch, based on commit d818363c530d233e9e8fc28f08a108ba17cff05b:
Sandbox | Source |
---|---|
Vanilla | Configuration |
Vanilla Typescript | Configuration |
rsk-github-issues-example | Configuration |
@examples-query-react/basic | Configuration |
reduxjs/redux-toolkit | Configuration |
@examples-action-listener/counter | Configuration |
I don't quite love that this signature requires the entirety of this function to either be defined in the same file as your store setup or called as a side-effect of importing it.
This is my current solution to those concerns, but feel free to give more feedback; you have more experience with teaching best practices.
export const setupListenersReactNative = (
dispatch: ThunkDispatch<any, any, any>
) => {
let initialized = false;
return setupListeners(
dispatch,
(dispatch, { onFocus, onFocusLost, onOnline, onOffline }) => {
let unsubscribeOnChange: NativeEventSubscription | undefined;
let unsubscribeOnNetworkStatusChange: NetInfoSubscription | undefined;
if (!initialized) {
// Handle focus events
unsubscribeOnChange = AppState.addEventListener("change", (state) => {
if (state === "active") {
dispatch(onFocus());
} else if (state === "background") {
dispatch(onFocusLost());
}
});
// Handle connection events
unsubscribeOnNetworkStatusChange = NetInfo.addEventListener((state) => {
if (state.isConnected) {
dispatch(onOnline());
} else {
dispatch(onOffline());
}
});
initialized = true;
}
const unsubscribe = () => {
unsubscribeOnChange?.remove();
unsubscribeOnNetworkStatusChange?.();
initialized = false;
};
return unsubscribe;
}
);
};
Well, that is how the existing setupListeners
is meant to be called.
Of course people can wrap that all they want in the end to accomodate for their code style/architecture, but the most basic thing here is that you should always call the existing setupListeners
function with a callback that works together with your use case.
I'm not sure if we gain a lot by dictating people on how exactly to wrap it 🤔
We could split it up another way a bit, but I'm not sure from the back of my head how that would play with TS:
import NetInfo from '@react-native-community/netinfo'
import { AppState } from 'react-native'
let initialized = false;
export const RNListeners = (dispatch, { onFocus, onFocusLost, onOnline, onOffline }) => {
let unsubscribeOnChange: NativeEventSubscription | undefined
let unsubscribeOnNetworkStatusChange: NetInfoSubscription | undefined
if (!initialized) {
// Handle focus events
unsubscribeOnChange = AppState.addEventListener('change', (state) => {
if (state === 'active') {
dispatch(onFocus())
} else if (state === 'background') {
dispatch(onFocusLost())
}
})
// Handle connection events
unsubscribeOnNetworkStatusChange = NetInfo.addEventListener((state) => {
if (state.isConnected) {
dispatch(onOnline())
} else {
dispatch(onOffline())
}
})
initialized = true
}
const unsubscribe = () => {
unsubscribeOnChange?.remove()
unsubscribeOnNetworkStatusChange?.()
initialized = false
}
return unsubscribe
}
// potentially in another file
setupListeners(
store.dispatch,
RNListeners
)
Seems it's cause infinity query loop on Android. I also tried 'focus' event instead of 'change' but it also calls multiple times. Were you able to handle this?
I don't quite love that this signature requires the entirety of this function to either be defined in the same file as your store setup or called as a side-effect of importing it.
This is my current solution to those concerns, but feel free to give more feedback; you have more experience with teaching best practices.
export const setupListenersReactNative = ( dispatch: ThunkDispatch<any, any, any> ) => { let initialized = false; return setupListeners( dispatch, (dispatch, { onFocus, onFocusLost, onOnline, onOffline }) => { let unsubscribeOnChange: NativeEventSubscription | undefined; let unsubscribeOnNetworkStatusChange: NetInfoSubscription | undefined; if (!initialized) { // Handle focus events unsubscribeOnChange = AppState.addEventListener("change", (state) => { if (state === "active") { dispatch(onFocus()); } else if (state === "background") { dispatch(onFocusLost()); } }); // Handle connection events unsubscribeOnNetworkStatusChange = NetInfo.addEventListener((state) => { if (state.isConnected) { dispatch(onOnline()); } else { dispatch(onOffline()); } }); initialized = true; } const unsubscribe = () => { unsubscribeOnChange?.remove(); unsubscribeOnNetworkStatusChange?.(); initialized = false; }; return unsubscribe; } ); };
Are you using ApiProvider
? That one had a bug in previous RTK versions that could subscribe to these multiple times.
Are you using
ApiProvider
? That one had a bug in previous RTK versions that could subscribe to these multiple times.
Hello, I use Provider from 'react-redux' (7.2.8). Tried different approaches but still no luck with refetching queries on Android when app state changes.
I found a similar issue in the "facebook/react-native" repo but it's still open without a solution https://github.com/facebook/react-native/issues/30206
Any advice will be highly appreciated
I'm sorry but if this is a RN thing I don't think we can really help with that.
Hi @msutkowski, is there any update on this? Would be nice to get this into the official docs:)