I have a react native app with two screens (actually too many): ScreenA and ScreenB. In ScreenA, I am doing some API calls and in ScreenB, I have some animation. I am using stack navigator to visit ScreenB from ScreenA. In ScreenB, I have some animation in my useEffect() hook and also from gestures. When I move away from ScreenB to ScreenA and then back to ScreenB, the state of the screen is not changed. It remains the same. I want the entire animation to reset, the useEffect() to be called again, but only for ScreenB. I want all my other mounted screens to be mounted, to avoid calling APIs and displaying data again but ScreenB to reset to its initial state if I move away from it. How can I achieve this in most efficient way possible? Thanks
You could use useIsFocused in a useEffect and reset the animation with something like
const isFocused = useIsFocused();
useEffect(()=>{
if(isFocused){
// start your animations
}
else{
// stop your animations
}
},[isFocused])
Related
Im using React Navigation x5, I have StackNavigator that's loading bunch of screens. I want to unmount one particular screen component on blur to get the same result of using unmountOnBlur when using a DrawerNavigator.
How can I achieve that?
There are currently three ways to do it since 5.x. You can either
use useFocusEffect hook to trigger an action (recommended), or
use useIsFocused hook, or
listen to the focus event
Example of useFocusEffect
import { useFocusEffect } from '#react-navigation/native';
function Sample(props) {
const [shouldHide, setShouldHide] = React.useState(false);
useFocusEffect(() => {
setShouldHide(false)
return () => {
setShouldHide(true)
}
});
return shouldHide ? null : <InnerComponent {...props} />;
}
Example of useIsFocused hook
import * as React from 'react';
import { Text } from 'react-native';
import { useIsFocused } from '#react-navigation/native';
function Profile() {
// This hook returns `true` if the screen is focused, `false` otherwise
const isFocused = useIsFocused();
return {isFocused ? <Text>Inner component</Text> : null};
}
Note:
Using this hook component may introduce unnecessary component re-renders as a screen comes in and out of focus. ...
Hence we recommend to use this hook only if you need to trigger a re-render.
More information can be found in the documentation Call a function when focused screen changes
Besides that, stack navigators come with a prop called detachInactiveScreens which is enabled by default. Link to the documentation
detachInactiveScreens
Boolean used to indicate whether inactive screens should be detached from the view hierarchy to save memory. Make sure to call enableScreens from react-native-screens to make it work. Defaults to true.
(The instruction on enabling React Native Screens can be found at https://reactnavigation.org/docs/react-native-screens/#setup-when-you-are-using-expo)
Inside the stack navigator's options object, there is also a detachPreviousScreen prop to customise the behaviour of certain screens. This option is usually enabled as well.
detachPreviousScreen
Boolean used to indicate whether to detach the previous screen from the view hierarchy to save memory. Set it to false if you need the previous screen to be seen through the active screen. Only applicable if detachInactiveScreens isn't set to false. Defaults to false for the last screen when mode='modal', otherwise true.
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("");
},[]);
I'm developing a react-native / redux app with a bottom-tab-navigator similar to the example at https://reactnavigation.org/docs/en/tab-based-navigation.html#customizing-the-appearance. My screens all connect to a Redux store and display shared data, however I'd like at least one of these screens to ignore the current data in the store and instead re-initialize this data each time it's navigated to (instead of continuing to display the data in whatever state it was last left in).
The screen has a method to do this, but I can't figure out how to call it after the first time the screen is rendered (e.g. from the constructor or componentDidMount() method). I can't call it from the render() method as this causes a "Cannot update during an existing state transition" error.
I need my navigator to somehow cause my HomeScreen.initializeData() method to be invoked each time the Home icon is pressed, but how do I do this?
HomeScreen.js:
initializeData() {
this.props.resetData(initialValue);
}
const initialValue = ...
(resetData() is a dispatch function that re-initializes the Redux store).
Updating state from render() would create an infinite loop. Also, you don’t want to run your state update every time the component re-render, only when the tab button is pressed. This tells me that the proper place to make your state update is some onPress function on the tab button.
So the question now relies on how to implement some onPress function on a tab button. I believe this answer this question:
Is there an onPress for TabNavigator tab in react-navigation?
So I found an answer, it's a little more complicated than might be expected: As Vinicius has pointed out I need to use the tabBarOnPress navigation option, but I also need to make my dispatch function available to this navigation option.
To do this I found I need to pass a reference to my dispatch function (which is available as a property of my screen) into the navigation option, so I've used navigation params to do this and here's what I've ended up with:
HomeScreen.js:
componentDidMount() {
initializeData(this.props);
this.props.navigation.setParams({ homeProps: this.props });
}
export const initializeData = (homeProps) => {
homeProps.resetData(initialValue);
};
const initialValue = ...
AppNavigator.js:
tabBarOnPress: ({navigation, defaultHandler}) => {
const routeName = navigation.state.routeName;
if (navigation.state.params === undefined) {
// no params available
} else if (routeName === 'Home') {
let homeProps = navigation.getParam('homeProps', null);
initializeData(homeProps);
} else if (routeName === ...
...
}
defaultHandler();
}
Notes:
I'm passing props as a navigation param rather than my dispatch function (which also works) as it's more flexible (e.g. it makes all of my dispatch functions available).
initializeData() is called both during construction of HomeScreen (for the first time the screen is displayed) and from the navigation icon (for subsequent displays of the screen).
It's necessary to check that params is defined within the navigation option as it'll be undefined the first time the screen is displayed (as screen construction has yet to occur). This also makes it necessary to call initializeData() during screen construction.
I am making an react native android app in which componentWillUnmount() does not works.Suppose user enters a text value in a text input in particular screen and goes to next screen.After that when user presses back button then i want that text value written in text input to be removed.I tries by adding this line in my code.
componentWillUnmount(){
this.setState({text:""})
}
Basically i was removing the state value which i put in TextInput.But this does not works.Same this is happening in case of activity indicator.When activity indicator start to fetch data then first i check if connected to internet or not.If not then by navigation i navigate to no network screen.But from there when i presses back button then activity indicator does not go away ? So how can i remove all the value of state in a component after navigating to different screen?
React Navigation allows you to add listeners so you track actions that are happening to the screen.
There are four listeners that you can add:
willFocus - the screen will focus
didFocus - the screen focused (if there was a transition, the transition completed)
willBlur - the screen will be unfocused
didBlur - the screen unfocused (if there was a transition, the transition completed)
https://reactnavigation.org/docs/en/navigation-prop.html#addlistener-subscribe-to-updates-to-navigation-lifecycle
Here is an example of calling the willBlur, the others follow the same pattern you just change the value that is passed to the function.
componentDidMount () {
this.didBlurSubscription = this.props.navigation.addListener(
'didBlur',
payload => {
// you can perform actions here when the screen `willBlur`
this.setState({text: ''});
}
);
}
componentWillUnmount () {
// remember to unsubscribe
if (this.didBlurSubscription) {
this.didBlurSubscription.remove();
}
}
The reason your current solution is not working is that the screen isn't being unmounted it is going in and out of focus/blur. So the componentWillUnmount will not be called. Also if the component is being unmounted then you should not be trying to setState as that is an anitpattern and can lead to memory leaks.
I am new in React Native ...just want ask nub question, my App wont triggered componentWillMount and componentDidMount when navigate back to other page,
I use react-native-router-flux for navigator
componentWillMount() {
this.props.searchRequest();
}
exp: I have homescreen and categories, from home I want go to categories: on categories I have that code, but sometime when I back to previouse page (homescreen) and back again on categories componentWillMount not triggered sometime,
I think my categories scene not unmounted
We can detect props change with
componentWillReceiveProps()
but we need set state to accommodate new props value,