How to refresh component in reactnative using redux pattern? - react-native

I am using Redux pattern, I have problem in state management.
Condition: When i successfully submitted form data from action.js with actonTypes.SUBMITSUCCESS. and i update view according to the status send from reducer.The problem is that when i press back from that component and again navigate to UpdateDate component the submit status remain same and every time it shows success whenever i navigate to that component.

in your reducer, after successfully sending the form data, is the state set back to its initial value?
also, I like to create an action "clearForm" and call it in the componentWillMount() to set the state back to is initial value:
componentWillMount() {
this.props.clearEntityForm();
}

Related

How to add redux selector state to useStack hook when moving back to original screen. react-native

First-of-all I apologise because I'm new to this concept of redux haha so bare with me here :)
I have implemented a very simple redux in my app
I have one screen here ListAnItem
Here there is a button.
Add size when pressing this they get navigated to another screen called SizeSelector
SizeSelector
here there is a button called dispatch size when they press this button it runs the following.
dispatch({type: "ADD_SIZE", payload: size })
this works perfectly with useSelector.
once this dispatch has been run I use navigation.goBack() to send the user back to the ListAnItem screen.
but once they come to this screen I want to add this useSelector redux state value to a useState hook something like this;
const [sizeState, setSizeState] = React.useState(null)
how can I change this state the minute something has been added to useSelector value of ADD_SIZE.
if that makes sense 😂.
Thanks,
Arnav.

track UI elements states with one object, but the states are not reserved once leaving the screen and coming back

In my react-native project, I have three checkboxes, I need to track the state of those checkboxes so I use an object with key-value (value is boolean) to represent the states of all three checkboxes and use useState hook to manage them. Here is my code:
import React, { useState, useEffect } from 'react';
...
const MyScreen = ({ navigation }) => {
// initially, all checkboxes are checked
const initialCheckBoxState = {
0: true,
1: true,
2: true,
};
const [checkBoxesState, setCheckBoxesState] = useState(initialCheckBoxState);
useEffect(() => {
return () => {
console.log('Screen did unmount');
};
}, [checkBoxesState]);
return (
<View>
...
<SectionList
sections={options}
renderItem={({ index, item }) => (
<CheckBox
onPress={() => {
const checkBoxesStateCopy = { ...checkBoxesState };
checkBoxesStateCopy[index] = !checkBoxesStateCopy[index];
setCheckBoxesState(checkBoxesStateCopy);
}}
/>
)}
/>
...
</View>
);
};
I omitted code that is not the concern of my problem. As you can see, for each item I draw one CheckBox component.
In practice, there are always three items (i.e. three check boxes to show). At the beginning I declared initialCheckBoxState, each key-pair represents the state of the checkbox of each. In the onPress callback of Checkbox I toggle each check box state & update the checkBoxesState by hook method setCheckBoxesState as a whole.
Everything works fine at runtime, my screen is re-rendered when toggling checkbox state, UI shows the status of checkboxes correctly. But issue comes when I navigate back to the previous screen and navigate back to this screen, all checkboxes states are back to the initial states.
So, why the checkboxes states are not reserved?
P.S. previous screen and MyScreen are under the same stack navigator. User press a button of previous screen to navigate to MyScreen. From MyScreen user can go to previous screen by pressing the "headerLeft" button
First lets answer the question:
why the checkboxes states are not reserved?
This component is handling its state completely independent, the state is created & handled inside and no values are passed-in from outside. what does it mean? this component has its initial state value inside of itself, it doesn't use any prop or anything else to initialize the state. everytime this component gets created, state is again initialized with that value. so that's the reason you lose all changes done to checkboxes, because when you leave this screen(component) , it gets unmounted(we'll talk about this in next question) and because all values are just handled inside, every data (containing checkboxes state) will be lost.
So now lets talk about this:
is react-native supposed to reserve the state when come back to the screen?
short answer is No. Every component is destroyed when unmounted including their state and data.
Now lets answer why
screens are still on the stack in memory, not destroyed?
Usually developers use a package like react-navigation or RNRF(which is built on top of react-navigation) for react navigation, most of times we don't care about how they handle this navigation logic, we just use the interface the provided us. each of these packages may have their own way to handle navigation. providing full answer to determine why exactly the screen in still in memory needs full code review and sure lots of debugging but i guess there are 2 possibilities. first as i said maybe the package you are using keeps the unmounted screens in memory at least for a while for some reason. the 2nd is a common react community issue which is Unmounted component still in memory which you can check at: https://github.com/facebook/react/issues/16138
And at last lets answer the question:
how do i keep checkboxes state even with navigating back and losing component containing their state?
This doesn't have just one way to that but simple and short answer is move your state out of the that component, e.g move it out to the parent component or a global variable.
to make it more clear lets explain like this: imagine screen A is always mounted, then you go in B and there you can see some checkboxes and you can modify the states. if the state is handled completely inside B, if you navigate back from screen B to A you lose all changes because B is now unmounted. so what you should do it to put checkboxes states in A screen then pass the values down to B. and when modifying the values, you modify A state. so when B gets unmounted all changes are persistant because you have them in A.
other approached exists as well, you can create a global singleton object named globalState. then put values needed to share between multiple screens there. if you prefer redux or mobx you can use them. one of their usages is when you have some data that you need to share between mutiple screens, these data are independent from where you are at and will persist.
This explanation is from official react-navigation documentation:
Consider a stack navigator with screens A and B. After navigating to
A, its componentDidMount is called. When pushing B, its
componentDidMount is also called, but A remains mounted on the stack
and its componentWillUnmount is therefore not called.
When going back from B to A, componentWillUnmount of B is called, but
componentDidMount of A is not because A remained mounted the whole
time.
https://reactnavigation.org/docs/navigation-lifecycle/#example-scenario
Your MyScreen screen is equivalent to screen B from the example, which means you can expect your screen to stay mounted if you navigate forward, but not backwards.
Its simple, just add a keyExtractor to your SectionList component, which would uniquely identify each checkbox, so that react knows which one to re-render on update.
You'll want to use AsyncStorage to persist data to the device. State variables will be cleared any time the component unmounts.
AsyncStorage docs:
https://react-native-community.github.io/asaync-storage/
import AsyncStorage from '#react-native-community/async-storage';
//You can only store string values so convert objects to strings:
const storeData = async (value) => {
try {
const jsonValue = JSON.stringify(value)
await AsyncStorage.setItem('#storage_Key', jsonValue)
} catch (e) {
// saving error
}
}
const getData = async () => {
try {
const jsonValue = await AsyncStorage.getItem('#storage_Key')
return jsonValue != null ? JSON.parse(jsonValue) : null;
} catch(e) {
// error reading value
}
}
UPDATE -
State is not being persisted due to the nature of React Component lifecycles. Specifically, when you navigate away from a screen the lifecycle method componentWillUnmount is called.
Here's an excerpt from the docs:
componentWillUnmount() is invoked immediately before a component is unmounted and destroyed. Perform any necessary cleanup in this method, such as invalidating timers, canceling network requests, or cleaning up any subscriptions that were created in componentDidMount().
...Once a component instance is unmounted, it will never be mounted again.
This means any values stored in state will be destroyed as well and upon navigating back to the screen ComponentDidMount will be called which is where you may want to assign persisted values back to state.
Two possible approaches aside from AsyncStorage that may work for some use cases to persist data across screens is using Context or a singleton.

Set state to initial state in react native

I would like to know if it's possible to set the state to the initial state values whenever we press back the button in the tabBar ? At the moment, when I leave the tabBar and come back after a few navigation in the app, the infos that the user enter in TextField persist.
Thanks !
You could use React hooks to achieve similar results to lifecycle methods in class functions.
The useEffect method runs on component render. You can set the state in there.
const [currentState, setCurrentState] = useState(null);
useEffect(()=>{
// This will run after 1st render
setCurrentState("");
},[]);

How to deal with state during a screen transition using react native and react navigation

I am getting an object undefined exception during a transition from one screen to another. I have a pretty good idea what is happening, I am just not sure if there is a clean way of handling it without just checking for undefined everywhere.
Here is my scenario:
Source screen references a user object in a Redux store
Navigation is initiated from the source screen to the destination screen.
The destination screen componentDidMount() is called where I clear the user object from the Redux store.
The source screen render() gets called again and the undefined error occurs because I cleared user from the store.
I am assuming that the source and destination screens have some overlap due to the transition. I have tried adding listeners with no luck. I can only get listener handlers to fire for type = action,
I am probably making this more complicated than it is, but I would like to find out if there is a standard way of dealing with this or if I am going about it in a completely wrong way.
Also, my react-navigation is integrated with Redux as per the instructions on React Navigation's website and the Redux React Navigation sample code.
Here is the basic code.
Screen 1
componentDidMount() {
// This is a Redux action that sets an object called user to null
clearUser();
{
Screen 2
render() {
return (
// Other code here
// user is null here because render gets called after Screen1#componentDidMount() gets called
<Text>{user.firstName + ' ' + user.lastName}</Text>
<TouchableOpacity
onPress={() => navigation.dispatch({ type:'NAV_SCREEN_1' })}
>
// button code here
</TouchableOpacity>
// Other code here
)
}
The type of error is "TypeError: Cannot read property 'firstName' of null"
I understand the error and why I am getting it. The user object has been set to null by my Redux action in componentDidMount() of Screen 1 and then Redux causes render of Screen 2 to get called which is referencing a user object which is null.
My question is, is there a special way to accommodate this behavior without having to check if the user object is null every place I need to use it ? Should I call clearUser() somewhere else ?
For the newest version of react-navigation, you can use either addListener, withNavigationFocus or isFocused to detect transition state of the screens.
Another unofficial way is to catch action type NavigationActions.COMPLETE_TRANSITION, in case of using middleware like redux-saga.
As an alternative, you can simply make your Redux-connected screen handle undefined or null state. I had a similar problem and I ended up rendering a loader instead of the screen content when user state is undefined. When I log out, the screen shows a loader for a fraction of a second, then the app navigates away.

React Native make network call before render

I have a component that displays Users in a ListView which needs to make a fetch call before it renders.
I navigate to this component using:
this.props.navigator.resetTo({ id: 'ListUsers', getusers: true });
In the ListUsers component I have componentWillReceiveProps so that I can make the fetch call before render.
However the method is never called.
Further from ListUsers I can navigate to the AddUser component.
From here either a new user is added or the user can Cancel.
If new user is added, I navigate back to the ListUsers component using:
this.props.navigator.resetTo({ id: 'ListUsers', getusers: true });
At this point componentWillReceiveProps is called.
if user cancelled then I navigate back to the ListUsers component using:
this.props.navigator.pop();
Can anyone help to see what I am doing wrong?
You perfectly described what the documentation says will happen when using componentWillReceiveProps
From the docs:
componentWillReceiveProps
Invoked when a component is receiving new props. This method is not
called for the initial render.
In your scenario, is there any reason why you can't use componentWillMount to make the fetch call? componentWillMount is called right before the component is rendered, and it is the first point you can call setState.
If the problem is you need to guarantee that render only shows certain data before the fetch is completed, there are some strategies to do that, but the simplest way may be to simply set a flag to false in your state in the constructor (if you are developing using ES6 methodology), and flip it to true after the fetch is complete. An if statement in your render method would round out this strategy.
If you want me to put together a more comprehensive example, I can do so.