Not getting updated redux-persist state inside React-navigation useFocusEffect hook - react-native

I am using react-navigation(5.6.1) with redux-persist(6.0.0) on my react-native project. One of my reducer is persisted(profile). I added a focus event listener to run some functions when my profile screen in focus.
In those functions, the profile state will be used in some logic. On some other parts of the component, there will be other function that will be dispatching action which will update the profile state.
The issue now is whenever I update the profile state, each time the screen is in focus, I will always get the previous persisted data in the event listener. However, the component always render the latest persisted state. Anybody have any idea why this is happening? Below is the snippet of the event lister.
useFocusEffect(
React.useCallback(() => {
console.log(profile);
dispatch(showErrorDialog());
return () => {};
}
}, []),

If you do not give dependencies then useCallback will call the memorised function, not the updated one.
useFocusEffect(
React.useCallback(() => {
console.log(profile);
dispatch(showErrorDialog());
return () => {};
}
}, [profile]))

Related

Remove listener AppState change react native

Good evening everyone, I am facing a problem.
I am developing an app in react-native and I need that, every time a user sends the app in the background or in an inactive state, when he returns to the app I force him to go to a certain screen (Loading) where I perform certain checks (such as if he is a blocked user, deleted, etc ...).
I have now written the following function
const [appState, setAppState] = useState(AppState.currentState);
useEffect(() => {
getAttivita();
getBanner();
const appStateListener = AppState.addEventListener(
"change",
(nextAppState) => {
setAppState(nextAppState);
if (nextAppState === "active") {
navigation.dispatch(
CommonActions.reset({
index: 0,
routes: [{ name: Routes.Loading }],
})
);
}
}
);
return () => {
appStateListener?.remove();
};
}, []);
I put this listener in the Screen Diary (which represents my home).
Now if from the screen Diary, I minimize the app, then I have no problems and everything works as it should.
However if I go to another screen and minimize the app, then I get the following error
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
in Diary (at SceneView.tsx:122)
Then when I log back into the app I realize that the listener for the app status is still active (so it is as if the remove () had not worked) and in fact I am pushed back into my loading screen.
So I'm wondering, is it the listener that isn't actually being removed?
Or am I doing something wrong?
Thanks in advance to who will answer me .

React Native Navigation: Navigate to a screen from outside a React component

https://wix.github.io/react-native-navigation/docs/basic-navigation#navigating-in-a-stack
indicates that pushing a new screen to the stack requires the current screen's componentId. My use case is to allow navigating based on certain events emitted by a native module. As such, componentId won't be available to me, since the event listener would reside outside any React component screen. Is there any way to navigate to a screen from outside in RNN? Or even get the current componentId.
I ended up adding a command event listener to store the current componentId in closure.
let currentComponentId;
Navigation.events().registerCommandListener((name, params) => {
if (name === 'push') {
currentComponentId = params.componentId;
}
});
Navigation.events().registerAppLaunchedListener(() => {
Navigation.setRoot(rootRouteConfig);
EventEmitter.addListener('navigate', (name) => {
Navigation.push(currentComponentId, {
component: {
name,
},
});
});
});

Redux or Pub/Sub?

I have a react app which will record audio.
When the user presses a button the component should cause it's parent to run a stopRecording() routine, resulting in saving the file and some json data about conditions and triggering a user flow.
I'd been looking into Redux which seems fine for JSON data but I'm less sure about audio files.
But does Redux fulfill the purpose of pub/sub for notifying components they should do stuff.
But does Redux fulfill the purpose of pub/sub for notifying components
they should do stuff.
Yes. Whenever the store changes, all "connected" components will receive the new store update and consequently componentDidUpdate will get called. So for example, let's say that you would like to trigger an action and listen on that trigger, then you would do something like this:
The subscriber
class MyListenerComponent extends Component {
...
componentDidUpdate(prevProps) {
if(this.props.triggerAction !== prevProps.triggerAction) {
// do something meaningful here, perform user flow, stop recording, whatever
}
}
...
}
const mapStateToProps = (state) => {
return({
triggerAction: state.triggerAction
})
}
export default connect(mapStateToProps)(MyListenerComponent)
The action triggerer (publisher)
this.props.dispatch({type: 'TRIGGER_ACTION', triggerAction: 'some data here'})
The reducer:
switch(action.type) {
case 'TRIGGER_ACTION':
return ({
...state,
triggerAction: action.triggerAction,
})
}

How to reset state of redux store while using react-navigation when react-navigation reset does not immediately unmount screens from stack?

I'm trying to do auth sign-out with react-native and am experiencing an issue where I want to reset the state of the redux store but, because I am using react-navigation, I have a bunch of redux-connected screens that are still mounted that re-render when the state tree is reset to it's initialState causing a bunch of exception errors. I tried to unmount them on sign-out with a react-navigation reset which redirects the user to the signup/login screen but I have no way of knowing when these screens are actually unmounted in order to call the RESET_STATE action. Initially I was dispatching the action via saga.
sagas/logout.js
import { LOGOUT, RESET_STATE } from 'Actions/user';
// clear localstorage once user logs out.
const clearData = function* clearData(action) {
AsyncStorage.removeItem('user');
yield put(
NavigationActions.reset({
index: 0,
actions: [
NavigationActions.navigate({ routeName: 'SignedOut' })
],
})
);
// causes re-renders, screens still mounted
yield put({type: RESET_STATE});
}
export default function* logoutSaga () {
yield all([
yield takeEvery(LOGOUT, clearData),
]);
}
I also tried to reset once user reaches the SignedOut screen in it's componentDidMount cycle but unfortunately the screens unmount at some point well after componentDidMount is triggered:
screens/SignedOut.js
import { resetState } from 'Actions/user';
import ActionButton from 'Components/FormElements/ActionButton';
class SignedOut extends Component {
// screens are still mounted, causing screens from
// previous screens to throw exception errors
componentDidMount() {
this.props.dispatch(resetState());
}
componentWillUnmount() {
// never called
}
handleSignup = () => {
this.props.navigation.navigate('Signup');
}
handleLogin = () => {
this.props.navigation.navigate('Login');
}
render() {
return(
<Container>
<ActionButton
text="Sign Up"
handleButtonPress={this.handleSignup}
/>
<ActionButton
text="Log In"
handleButtonPress={this.handleLogin}
/>
</Container>
);
}
}
export default connect()(SignedOut);
My question is, can anyone think of a way to reset state of redux store after all of my screens have finally unmounted by the react-navigation reset action?
The issue is you're using navigate to navigate to the login/signup screen which leaves all your other components mounted, you should probably use back or reset to unmount all the components and show the login screen.
After thinking about this for a long time, I figured out that maybe I should have been focusing on the errors thrown instead of why I was getting errors. (I did learn a lot though).
Although figuring out how to listen for when all the screens are completely unmounted after calling a reset would have been super helpful, it would have just been a shortcut to bypass the real issue, the initialState for certain branches of my redux state was wrong. After correcting this no more errors, no matter when react-navigation decides to unmount the old screens.
As i have no idea how ur state looks like, which is always the issue here, why not try to use componentWillUnmount on all those components, to set a state variable and check that when you want to reset navigation?

Waiting for async before register component in react-native

I need to wait for async storage and then init app, because I store auth token here and want to show correct scene for user if he was authorised:
(async () => {
const viewer = JSON.parse(await AsyncStorage.getItem('viewer'));
// ...
const RootContainer = () => (
// ...
);
AppRegistry.registerComponent('yawaloo', () => RootContainer);
})();
I have moved to react-native 0.40.0 from 0.34.1 and now have an error "Module AppRegistry is not a registered callable".
In previous version everything were ok. How can I wait for some actions and then start render RootContainer?
One idea is to use splash screen. More specifically use a state in your RootContainer to determine whether to show a splash screen or your main UI. Set the state to false (show splash) initially then after you read the token from async storage, then set the state to true.
Part of why apps have splash screens is to deal with situation like this. HTH