React Native - Triggering useeffect from another screen - react-native

I have a table that has items in it. When I click a specific item's edit button. It goes to edit page but when I submit an edit of that item. It goes back to main table page but the table page won't be updated. So, I think I need to trigger the useeffect function. To do that I need to update the state in main table page from another screen that is edit page screen.
my apps are not class-based. all of them functional. Here are my codes.
Main Table Page:
//I created a state to update
const [reload,
setReloader] = useState('');
I try to send the state to change it in edit item screen.
<TouchableOpacity onPress={() => navigation.navigate('addProduct', [reload])}>
Edit Item Page:
const [reload,
setReloader] = useState(route.params.reload); //I tried to pass state but it didn't work like that.

Pass a function through navigation
Pass the function in which you will be updating the state. i.e. setReloader
<TouchableOpacity onPress={() => navigation.navigate('addProduct', {setReloader})}>
On addProduct screen get the function by
const setReloader = route.params.setReloader;
once you have edited simply call the setReloader function on the edit page and then go back to the main screen
Using the navigation lifecycle method.
On the main screen, you can add a focus lister
const focusListner = navigation.addListener('focus', () => {
//call the API to fetch the updated data from the server
});
Focus listener is called whenever the return to that screen

You can do something like:
//I created a state to update
const [reload, setReloader] = useState('');
const updateState = (value) => {
setReloader(value);
}
Send `setReloader` as callback to update on edit screen
<TouchableOpacity onPress={() => navigation.navigate('addProduct', {callback: updateState})}>
Edit Item Page:
const callback = route.params.callback;
// call `callback` which will update view on parent screen

import { useIsFocused } from "#react-navigation/native";
const isFocused = useIsFocused();
Well it is worked for me. To trigger.
useEffect not called in React Native when back to screen

Related

"Undefined" error when passing params to another screen in React Navigation

I have 2 screens, EditProfile and Profile. I'm trying to pass the edited name and date from EditProfile to Profile. They are both stacked in a ProfileNavigator, Profile is the first screen that appears and from there, user can tap on a edit profile button and navigate to Edit Profile Screen. How can i pass updated Edit Screen name and date params to main profile? Following code does not work and return Cannot read property "date" of undefined:
const EditProfileScreen = () => {
const navigation = useNavigation();
}
return (
..
<TouchableOpacity style={styles.commandButton} onPress= {() => {navigation.navigate("Profile", {params: {date: date, name: name }})} }
activeOpacity={0.5}>
<Text style={styles.panelButtonTitle}>Submit</Text>
</TouchableOpacity>
);
While this is the code for the Profile Screen:
const ProfileScreen = ({route, navigation}) => {
const { date } = route.params;
const { name } = route.params;
return (
..
<Text>{JSON.stringify(date)}</Text>
<Text>{JSON.stringify(name)}</Text>
)
There is nothing wrong with your approach, but you are missing null handling for the initial render of the profile screen.
Think like this, the first time you navigate to the profile screen there wont be a route.params as you are not passing any parameters which ends up in an error.
But when you move to the edit screen and come back with parameters you will have the route.params which you can access without any issues.
So change your code like this
<Text>{JSON.stringify(route.params?.date)}</Text>
<Text>{JSON.stringify(route.params?.name)}</Text>
Or if you have the profile object in the state of the profile screen, update it using a useEffect hook based on the change of route.params.

Render useEffect/Async function from a difference screen

I have an async function and a useEffect that fetches data once.
const [data, setData] = useState([]);
async function fetchData() {
fetch(`${baseURL}api/v1/data/${userId}`)
.then((response) => response.json())
.then((response) => {
try {
if (response.length > 0) {
setData(response);
} else {
setData([]);
// console.log(response);
}
} catch (err) {
console.log('no response');
alert(err);
}
});
}
useEffect(() => {
fetchData();
}, [userId, data]);
I could remove the array on the use effect but it will always run the function if I do that.
So when I open the screen, it will fetch the latest data. However, if I want to add a new data from a different screen, it wont trigger the async nor the useEffect function. How should I tell RN that there is a new data? Would AsyncStorage work? to update a data from one screen and apply the data here? I am open for suggestions on how to proceed.
What I meant by a different screen: A register screen and a view screen. In this case, I already opened the View Screen before I open the register screen so view screen is already rendered.
In React Navigation and most of the navigation libraries, screens don't get unmounted from the stack when it's navigated to another screen. For example if you have a list of something and then you press to "+" button to navigate to the "new item" screen to add a new one, when you press back button, since the previous "list" screen was not unmounted from the stack, useEffect won't be triggered, and you won't get the new data.
There are a couple of solutions for this case:
You can hold your data in a global state, and when you update an item from another screen, after a successful API call, you can also update the global state. You can look for React Context, MobX or Redux for this.
You can pass parent's state with a callback from one screen to another if they are not that apart from each other. So that in the "new data" screen, you can call that callback function to change the parent screen's state too.
Third, and IMO the best way is using a hook called useFocusEffect by React Navigation itself: https://reactnavigation.org/docs/use-focus-effect
I hope these will help.

How can I create a React Native Button that saves an array value and the Button state to the device using AsyncStorage?

I am trying to create a button that changes the text when clicked and it saves a value to an array. I then need to save the state of the button and the array to the device using AsyncStorage.
I have the first part done (please see Snack link here) but I am struggling to get the AsyncStorage to work properly. If the button had been clicked once, I would expect that the array has the "item 1" value in it and the button would say clicked. Even if the app is closed and reopened, those values should still remain until the button is clicked again.
I have not found any solutions so far. Is there anyone that has some ideas?
This is the workflow that you should follow:
create an object in state (in this.state = {} for classes, or setState() with hooks) for your button's text value
initialize the above value with the value from AsyncStorage (make sure to add some conditional that if it's empty it returns [])
also take note of how Asyncstorage is async, meaning you'll have to add 'await' when you're assigning the value
add some conditional for the text value of your button, whereas it will show a loading icon, (or nothing, doesn't matter) while AsyncStorage is retrieving the initial data
onPress on your button will change the state value AND the AsyncStorage value (or you can only update the AsyncStorage value when you're closing the page with componentWillUnMount, or useEffect(() => return(--do it here--))
If you're using functional components, it would look something like this:
const [textValues, setTextValues] = useState([])
const setInitialValues = async () => {
const info = await AsyncStorage.getItem('myValues')
setTextValues(info)
}
useEffect(() => {
setInitialValues()
return(
AsyncStorage.setItem('myValues', textValues)
)
}, [])
return(
<View>
<Button onPress={() => setTextValues(textValues + 1) title={textValues.length === 0 ? '' : textValues[textValues.length - 1]}}
</View>
)

Expo React Native execute function when entering view

I'm trying to make a function execute when a view is in foreground, but just once not on each update of the component. If the user navigates to another view and goes back to the first view it should execute that function again, but just once. Is there a solution to this?
if using useEffect without second parameter it executes on each update, if I add [] as second parameter it only executes the first time the view is rendered but not when navigating back to it.
Any help appreciated!
if you are using react-navigation you can do this by listen on screen focus see here
React.useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
// The screen is focused
// Call any action
});
// Return the function to unsubscribe from the event so it gets removed on unmount
return unsubscribe;
}, [navigation]);

Pass variables via `this.props.navigation` multiple times

So, for begging, in react-native-navigation there's a possibility to pass some data via this.props.navigation.navigate().
Here's how you should pass the data :
this.props.navigation.navigate('RouteName', {/*Data to pass*/})
And so, moving to the problem
The case where this problem was encountered :
I have a list of items which I click on and I navigate to the next screen, the data of the pressed item being sent during the navigation process, and when I get to the next screen, the passed data is assigned to state, and I further operate with it. Here are the commands which I use for passing data:
Pass data
this.props.navigation.navigate('Screen2',{param1: value1, param2: value2})
Receive data
ComponentWillMount = () => {
const param1 = this.props.navigation.getParam('param1');
const param2 = this.props.navigation.getParam('param2');
this.setState({param1, param2)}
}
The Problem itself
My Problem is that if I go back to the first screen, and press on another item, then it's data isn't passed via this.props.navigation.navigate(), the data on the second screen remains unmodified from the first navigation process. How this problem can be resolved?
I think i figured it out,
I was able to replicate the issue using drawerNavigator and tabbed navigator in the react-navigation 3.0.5.
Basically they save the components even when you run navigation.goBack.
The screen isn't being mounted again so it doesnt call componentWillMount() and it doesn't check for data there.
there are 2 (edit 3) ways to fix this.
one is to turn off this performance enhancement
const MyApp = createDrawerNavigator(
{
Screen1: Screen1,
Screen2: Screen2
},
{
unmountInactiveRoutes: true
}
);
The second option and the more elegant one is to subscribe to navigation events
componentWillMount() {
console.log("mounting");
const willFocusSubscription = this.props.navigation.addListener(
"willFocus",
() => {
console.debug("willFocus");
const thing = this.props.navigation.getParam("thing");
const thing2 = this.props.navigation.getParam("thing2");
this.setState({thing, thing2});
}
);
}
Just dont forget to unsubscribe in componentWillUnmount
componentWillUnmount() {
willFocusSubscription.remove();
}
The third way is basically the same as the second but subscribing declaratively. This means no componentWillMount or WillUnmount.
First a callback to set the state appropriately
willFocus = ({action}) => {
console.debug("willFocus", action);
const thing = action.params["thing"];
const thing2 = action.params["thing2"];
this.setState({thing, thing2});
};
now in render add the component
render() {
console.log("data is:", this.state.thing);
return (
<View style={styles.container}>
<NavigationEvents
onWillFocus={this.willFocus}
/>
.... rest of render body
</View>
);
}
This doesn't display anything but it takes care of subscribing and unsubscribing.