I noticed that whenever I navigate to another page using the navigate props available to my component, it triggers a re-render of the component and componentDidMount is being called whenever I navigate to a screen that has rendered before.
For instance, when I navigate a user to their profile page and they decided to go back to the dashboard, the dashboard component which has been initially rendered is being rendered again and componentDidMount is being called thereby slowing down the application.
import { StackNavigator } from 'react-navigation';
const Routes = StackNavigator({
home: {
screen: HomeScreen
},
dashboard: {
screen: Dashboard
},
profile: {
screen: Profile
}
},
{
headerMode: 'none',
});
In my component I navigate the user with this.props.navigation.navigate('ScreenName')
I would appreciate any help to stop the component from re-rendering when navigating back to it. Thanks
I would have a state variable in your constructor that keeps track if you navigated. State is only relevant to the current component. So if you navigate to 'ScreenName' multiple times, the stack builds and each ScreenName component has its own state.
constructor(props)
super(props)
this.state = {
navigatedAway : false
}
Then before you navigate to your 'ScreenName' screen update the state
this.setState({
navigatedAway : true
},
() => {
this.props.navigation.navigate('ScreenName');
}
);
Use syntax above to make sure state isUpdated THEN navigate. Then like Dan said in comments above if your function shouldComponentUdate have a condition statement.
shouldComponentUpdate(newProps){
// return true if you want to update
// return false if you do not
}
* Side Note *
When you navigate I don't believe the component is unmounted. You could verify this by simply printing to console. Correct me if I am wrong though, I am fairly new to react native.
componentDidMount() {
console.log("COMPONENT_CONTENT_MOUNTED")
}
componentWillUnmount({
console.log("COMPONENT_CONTENT_UNMOUNTED")
}
If you are using React Navigation 5.X, just do the following:
import { useIsFocused } from '#react-navigation/native'
export default function App(){
const isFocused = useIsFocused()
useEffect(() => {
//Update the state you want to be updated
} , [isFocused])
}
If I understand your question correctly, when you navigate away, the component is unmounted.
When you navigate back, it must be re-mounted, hence re-rendered.
In general, any UI change necessitates a re-render. No way around that - It's kind of "by definition".
You might be able to cache the page.
Or use the reselect library to cache expensive to obtain data, so the calculations for re-rendering are quick and minimal.
If react/react-native thinks the props have changed (in an already mounted/rendered component), it will also re-render, but you can influence this decision via shouldComponentUpdate().
Just add React.memo to your export of component that reload each time.
So instead of
export default component
you would have:
export default React.memo(component);
Which does a comparison of props and only re-renders if props change (so not on navigate but on actual changes, which is what you want)
Related
In my code I have a problem with unmount a component. I am using drawer-navigator and when i navigating between drawer-screens the previous screen is not dying and when i open that screen again everything is still there.
But i want to re-render or unmount and mount the component again when i navigate between drawer-screens. Is there a way for that? Or how can i make my component unmount manually? I know there is a lot of question about this but i couldn't find that i want.
My react native version is 0.63
react navigation never destroy the screen. Its mean your screen will never unMount or when you go back it will never remount (didMount). To Perform such see the example below
import React, { useCallback } from 'react';
import { useFocusEffect } from '#react-navigation/native';
const Home = () => {
useFocusEffect(
useCallback(() => {
// Do something when the screen is focused
return () => {
// Do something when the screen is unfocused
// Useful for cleanup functions
};
}, [])
);
return <Home />;
}
Reference : Navigation Lifecycle
You should check this page of 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.
Conclusion, the screens are not detroyed and re-created everytime you navigate, it's by design.
If you want to execute some code when you leave or open again a page you should use the lifecycle events presented in the same documentation
This is my app container:
const AppNavigator = createBottomTabNavigator({
createUser: CreateUser,
showUsers: ShowUsers,
editUser: EditUser
});
in createUser I make new users and save them in my database, and in showUsers on componentDidMount lifecycle I get the data from my database and list them. but since screens don't unmount when we navigate away from them, the componentDidMount lifecycle is just called the first time, so each time I get previews data, and I have to reload my app to see the result.
how can I solve this?
React navigation has a lifecycle api, to do exactly what you describe:
For more information check the docs: https://reactnavigation.org/docs/en/navigation-lifecycle.html
Part of that API is a withNavigationFocus HOC, and this provides an isFocused prop which indicates if a screen is visible.
Example:
import { withNavigationFocus } from 'react-navigation';
class SomeScreen extends React.Component {
componentDidUpdate(prevProps) {
if (this.props.isFocused && !prevProps.isFocused) {
// Screen has now come into focus
}
}
}
export default withNavigationFocus(SomeScreen)
This is my component's class:
import React, { Component } from 'react';
import {connect} from 'react-redux';
import Button from '../UI/Button/Button';
import * as actions from '../../store/actions';
class Password extends Component {
submitPassword=(e)=>{
e.preventDefault();
this.props.submitPassword(this.state.password, this.props.levelNumber);
}
render() {
<Button clicked={this.submitPassword} >
Submit password
</Button>
}
}
const mapStateToProps = state => {
return {
};
}
const mapDispatchToProps = dispatch => {
return {
submitPassword: (password,levelNumber) => dispatch(actions.submitPassword(password,levelNumber))
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Password);
and this is my action:
export const submitPassword = () => {
// HERE ALL MY LOGIC !!!
return {
level:undefined,
type:actions.PASSWORD
}
}
The code working all correctly including params and logic.
I wanna that every time that finish to execute the function submitPassword A third component refresh/reload with the new props. Attention! My third component is parent, not child!
It's possibile to send a command from action to component? How can I do it? I have already tried with:
componentWillReceiveProps() {
console.log("new props");
}
in my component but he can not take the event.
normally a structure my redux store as follows
{
entities: {},
ui: {},
domain:{}
}
so for example when you execute submitPassword you execute all the logic and depending on the result you can dispatch another action to update the ui or the domain part so the componentes that are connected respond to those changes.
The UI holds information about UI changes, like if you are submiting info display a progress bar or if it has to display a dialog.
The domain part holds information related to the whole webapp, like loggin info and notifications.
You don't need always to pass new props for redux state to be accessed.
As redux is having immutable state, you'll always be getting new updated state no matter the previous one. So this will enforce your component to update props to get latest state from redux. This is done by <Provider> wrapper attached on root level.
So hence your props will be having new values whenever redux state gets updated.
The lifecycle you are looking is static getderivedstatefromprops(). This lifecycle gets executed whenever props are changed/updated.
I made visual implementation on fly that can aid you the picture. Here Redux State means Redux Store
I am implementing a react page with route info/:id. From this page, there are various URLs which are navigated by NavLink.
For example: if current route is localhost:3000/info/1 and there are links localhost:3000/info/2 and localhost:3000/info/3, clicking on those links will load the page I'm currently on i.e: localhost:3000/info/1. While tracking for props changes, url is getting changed briefly but it's again loading the same page. What shall I do to route to same component with different parameters?
Thanks in advance!!!!!!
You should track your page updates in componentDidUpdate lifecycle method or useEffect hook.
Class components:
componentDidUpdate(prevProps) {
if(prevProps.match.params.id !== this.props.match.params.id){
// do something
}
}
Functional components:
useEffect(() => {
// do something
}, [props.match.params.id]);
Here's how you achieve same via React hooks and functional components:
useEffect(() => {
//do something
}, [props.match.params.id]);
I have a Component say HomePage where I'm calling getCurrentLocation to get the location using GPS. This value will be used to select a value from a Drop Down. User may then use the dropdown to change the value.
When I navigate away from this page and then come back using
const appHistory = createHashHistory();
appHistory.goBack();
the constructor and the componentDidMount are executed again. So the user selected value is lost and i get the default value again.
So where do I put by initialisation code? I come from Ionic where there something like ionViewDidLoad which is executed only when the page loads the first time. Is there an equivalent for this is React.
Below is my code
export class HomePage extends React.Component {
constructor(props){
super(props);
state = {
currentLocationCoordinates:[],
};
this.getCurrentLocation = this.getCurrentLocation.bind(this);
}
getCurrentLocation(){
navigator.geolocation.getCurrentPosition((success)=>{
console.log("Current Location: "+success.coords);
this.setState({
currentLocationCoordinates:[success.coords.latitude,success.coords.longitude],
userLocationCoordinates:[success.coords.latitude,success.coords.longitude]
});
});
}
}
componentDidMount(){
this.getCurrentLocation();
}
}
I would avoid using componentDidMount for this. Make your components as resilient to re-renders and re-mounts as possible.
By the sounds of it you are after an application level state container that will hold your application state regardless of whether your component is mounted or not.
Most of the React community relies on Redux for this although other state containers do exist. I'd suggest having a look at Redux and using it to hold those location details as part of Redux store. They will then be always accessible on your component as props, regardless of whether it re-renders or not.
You need to use ComponentDidMount() together with a flag that detects whether the component has been initialized or not. You are on the right track.
componentDidMount(){
initialized ? "" : this.getCurrentLocation();
}