65

I am new to React Native. How can we refresh/reload previous screen when returning to it by calling goBack()?

Lets say we have 3 screens A, B, C:

A -> B -> C

When we run goBack() from screen C it goes back to screen B but with old state/data. How can we refresh it? The constructor doesn't get called 2nd time.

Lakshmikant Deshpande
  • 826
  • 1
  • 12
  • 30
Adnan Ali
  • 2,890
  • 5
  • 29
  • 50
  • Have you got the solution of this ? Please let me know, I was also facing the same. I have resolved it. – Deepak Sachdeva Nov 10 '17 at 09:45
  • Yes @DeepakSachdeva thanks but you may share your findings for the knowledge. Following did help me https://stackoverflow.com/a/46892405/2086124 – Adnan Ali Nov 17 '17 at 07:02
  • I wonder how you specifically solve this problem since I have the same problem. You refer to the answer below, but which one of the three options that user @Bat presented? It would be much better also, if you answer your own question with a detail on how you solve it. Since, this would really help those who will have the same problem as you do in the future. Hope you can post it. GOD bless. – Edper Jan 20 '18 at 07:46

19 Answers19

98

Adding an Api Call in a focus callBack in the screen you're returning to solves the issue.

componentDidMount() {
    this.props.fetchData();
    this.willFocusSubscription = this.props.navigation.addListener(
      'willFocus',
      () => {
        this.props.fetchData();
      }
    );
  }

  componentWillUnmount() {
    this.willFocusSubscription.remove();
  }

UPDATE 2023: willFocus event was renamed to focus

  componentDidMount() {
    this.props.fetchData();
    this.focusSubscription = this.props.navigation.addListener(
      'focus',
      () => {
        this.props.fetchData();
      }
    );
  }

  componentWillUnmount() {
    this.focusSubscription();
  }
Mukundhan
  • 3,284
  • 23
  • 36
  • 1
    This should be the answer. Works perfectly if put into the correct screen (screen about to be shown when back button is pressed). Also includes non-deprecated methods. Nice! :) – Imdad Mar 25 '19 at 09:19
  • While we were working on a project data fetching was a key thing on pressing back button. Thankfully it is solved on time :) – Mukundhan Apr 02 '19 at 05:22
  • 2
    `Mukundhan`, will `willFocus` automatically be launched after `goBack()`? Also are all the re-fetching data goes into `fetchData()`? – user938363 Jul 17 '19 at 17:55
  • Yes, it will be called on clicking back. Only the details that might get changed on navigating to a new page can be fetched in the fetchData() – Mukundhan Jul 18 '19 at 11:32
  • 2
    Yes, `addListener` on the `navigation` object with the `willFocus` event did the trick. Thank you! – HartleySan May 19 '20 at 15:34
  • 4
    Had to use just 'focus' in order for it to work at all. 'willFocus' and 'didFocus' did not work –  Jun 23 '20 at 22:05
  • 1
    When unsubscribing i had to ``this.willFocusSubscription()`` as mentioned in docs: https://reactnavigation.org/docs/navigation-events/#navigationaddlistener – SAM Mar 23 '21 at 11:12
  • how to reset all sate in functional component – Sagar Nov 11 '21 at 08:08
  • 1
    This refreshes all the time when refocusing, which may not be the desired result for many cases. For example, I only want to refresh a list when a save action was taken, not a back action. – Rob Feb 01 '22 at 09:13
  • yes, we had that use case too, we used a global variable in the store and we will set that to true whenever we need to refresh. you can also try using a static variable if possible – Mukundhan Feb 01 '22 at 09:15
76

How about using useIsFocused hook?

https://reactnavigation.org/docs/function-after-focusing-screen/#re-rendering-screen-with-the-useisfocused-hook

const componentB = (props) => { 
  // check if screen is focused
  const isFocused = useIsFocused();

  // listen for isFocused, if useFocused changes 
  // call the function that you use to mount the component.

  useEffect(() => {
    isFocused && updateSomeFunction()
  },[isFocused]);
}
Onur Eker
  • 841
  • 8
  • 11
  • 3
    This is the most easiest way for beginners. I don't know why this answer is not on the top. – rafwell Dec 07 '20 at 17:32
  • 2
    @rafwell l am not a beginner but l love the simplicity of the answer. – CanCoder Jan 18 '21 at 21:34
  • 3
    Works for me, thank you. I just added `useEffect(() => {isFocused && updateSomeFunction() },[isFocused]);` to only fetch the data when is focused – Colo Ghidini May 05 '21 at 20:17
  • Of all the answers here, this is the only answer that worked for me in 2021. isFocused hook updates reliably - just make sure you check for when it's true within the hook as in the comment above. – Niko Dunk Aug 03 '21 at 18:17
26

For react-navigation 5.x use

5.x

use

componentDidMount() {
  this.loadData();

  this.focusListener = this.props.navigation.addListener('focus', () => {
    this.loadData();
    //Put your Data loading function here instead of my this.loadData()
  });
}

For functional component

function Home({ navigation }) {
  React.useEffect(() => {
    const unsubscribe = navigation.addListener('focus', () => {
      loadData();
      //Put your Data loading function here instead of my loadData()
    });

    return unsubscribe;
  }, [navigation]);

  return <HomeContent />;
}
Farrukh Normuradov
  • 1,032
  • 1
  • 11
  • 32
kvadityaaz
  • 1,441
  • 1
  • 16
  • 23
16

On your screen B constructor will work like magic :)

    this.props.navigation.addListener(
          'didFocus',
          payload => {
            this.setState({is_updated:true});
          }
    );
Brijesh Singh
  • 416
  • 4
  • 8
  • hello brother, please can you provide me a sample code.. I am also facing this issue. – Rishav Kumar Dec 12 '18 at 09:11
  • @RishavKumar This is a hack... just create a constructor if you don't have have one and paste above code. Actually this code is adding a listener and everytime component got focus it re-renders the views. – Brijesh Singh Dec 13 '18 at 10:14
  • For anyone trying to use this => `willFocus` event was renamed to `focus`. Can't edit the answer as to there are too many pending edits. – Sercan Samet Savran Feb 16 '23 at 09:27
8

Yes, constructor is called only for the first time and you can't call it twice.

First: But you can separate the data getter/setter from the constructor and put it in a function, this way you can pass the function down to the next Scene and whenever you're going back you may simply recall the function.

Better: You can make a go back function in your first scene which also updates the scene while going back and pass the go back function down. This way the second scene would not be aware of your update function which is reasonable.

Best: You can use redux and dispatch a go-back action in your second scene. Then in your reducer you take care of going back & refreshing your scene.

Bat
  • 771
  • 11
  • 29
  • 3
    Probably when you wrote this answer reactn wasn't there. Do yourself a favour guys: drop redux (for good) and switch to [reactn](https://www.npmjs.com/package/reactn). – Luca Fagioli Jan 11 '21 at 08:30
  • @LucaFagioli It has been years since I used to write RN. Can you please update this answer according to this new technology? I would do it if I had the context – Bat Jan 17 '21 at 01:41
  • 2
    I wasn't referring to this specific question, but to the enthusiastic tone about redux, which should be rather mentioned as one the most overcomplicated approach in the software history (source: 25 years of software development). As you can see from the answers below, someone else already found a 2-3 lines solution to solve the very same problem. – Luca Fagioli Jan 17 '21 at 09:18
5

The built in listener function which comes with React-Navigation would be the easiest solution. Whenever a component is 'focused' on a again by navigating back, the listener will fire off. By writing a loadData function that can be called both when loading the Component AND when the listener is notified, you can easily reload data when navigating back.

   componentWillMount(){
    this._subscribe = this.props.navigation.addListener('didFocus', () => {
     this.LoadData();
     //Put your Data loading function here instead of my this.LoadData()
    });}
RawBData
  • 207
  • 4
  • 4
5

Easy! insert the function inside useFocusEffect(func)

import { useFocusEffect } from '@react-navigation/native' 
Suraj Rao
  • 29,388
  • 11
  • 94
  • 103
Turjoy Saha
  • 107
  • 1
  • 4
4

This can be achived by useFocusEffect from '@react-navigation/native'

useFocusEffect will effect every time when screen is focus

Ref: https://reactnavigation.org/docs/use-focus-effect/

import { useFocusEffect } from '@react-navigation/native';

function Profile({ }) {
  
  useFocusEffect(
    React.useCallback(() => {
      //Below alert will fire every time when profile screen is focused
        alert('Hi from profile')
    }, [])
  );

  return // ...code ;
}
Hardik Desai
  • 1,089
  • 12
  • 20
3

I have a similar situation and the way i refreshed was to reset the route when the back button is pressed. So, what happens is when the back button is pressed the screen is re-pushed into the stack and the useEffect on my screen loads the data

navigation.reset({
  index: 0,
  routes: [{ name: "SCREEN WHERE THE GOBACK BUTTON SHOULD GO" }],
});
ehtulhaq
  • 59
  • 7
3

Update for react-navigation v5 and use the React Hooks. Actually, the use is the same with react base class. For more detail, please checkout the documentation here

Here is the sample code:

function Profile({ navigation }) {
  React.useEffect(() => {
    const unsubscribe = navigation.addListener('focus', () => {
      // do something
    });

    return unsubscribe;
  }, [navigation]);

  return <ProfileContent />;
}

As above code, We add the event listener while the variable navigation change then We do something like call function refresh() and finally, we return the function for removing the event listener. Simple!

nahoang
  • 2,400
  • 1
  • 18
  • 22
3

I think we have a very easy way (which works in 2021) to do so. Instead of using goBack or navigate, you should use push

this.props.navigation.push('your_route_B'). 

You can also pass params in the same way as we pass in navigate.

The only difference b/w navigate and push is that navigate checks if the route which we are passing exists in the stack. Thus taking us to the older one but, push just sends us there without checking whether that is in the stack or not (i.e, whether the route was visited earlier or not.)

Irfan wani
  • 4,084
  • 2
  • 19
  • 34
2

You can use this event: navigation.addListener('focus'

And you can implement like this:

const Cards = ({ navigation }) => {
...
   useEffect(() => {
        const load =async ()=>{
            const a = await selectGlobalCards()
        }
        navigation.addListener('focus',() =>{
            load();
        });
    }, [])

or you can use useIsFocused, and you can use that as a dependecy for useEffect

import { useIsFocused } from '@react-navigation/native'
const Cards = ({ navigation }) => {
    const isFocused = useIsFocused()
    useEffect(() => {
        const load =async ()=>{
            const a = await selectGlobalCards()
        }
       
        load()
    }, [isFocused])
rnewed_user
  • 1,386
  • 7
  • 13
1

For react navigation (5.x), you just need to add a focus subscription and put your component initializing logic in a separate function like so:

componentDidMount() {

    this.init();

    this.didFocusSubscription = this.props.navigation.addListener(
      'focus',
      () => {
        this.init();
      }
    );


  }


init = async () => {
   //fetch some data and set state here
   
  }
Acheme Paul
  • 1,194
  • 15
  • 19
0

If you're trying to get new data into a previous view, and it isn't working, you may want to revisit the way you're piping data into that view to begin with. Calling goBack shouldn't effect the mounting of a previous component, and likely won't call its constructor again as you've noted.

As a first step, I would ask if you're using a Component, PureComponent, or Functional Component. Based on your constructor comment it sounds like you're extending a Component class.

If you're using a component, the render method is subject to shouldComponentUpdate and the value of your state is in your control.

I would recommend using componentWillReceiveProps to validate the component is receiving the new data, and ensuring its state has been updated to reflect the new data.

If you're using the constructor to call an API or async function of some kind, consider moving that function into a parent component of both the route you're calling goBack from and the component you're wanting to update with the most recent data. Then you can ask your parent component to re-query the API, or update its state from a child component.

If Route C updates the "state/data" of the application, that update should be propagated to a shared parent of routes A, B and C, and then passsed down as a prop.

Alternatively, you can use a state management solution like Redux to maintain that state independent of parent/child components - you would wrap your components in a connect higher-order component to get the latest updates any time the application state changes.

TL;DR Ultimately it sounds like the answer to your question is rooted in where your application state is being stored. It should be stored high enough in your component hierarchy that each route always receives the latest data as a prop, passed from its parent.

joel.software
  • 1,505
  • 1
  • 12
  • 15
0

Thanks to @Bat. I have spent a lot of hours on finding the answer and finally, I got a basic solution which is working according to my needs. I was quite worried though. Simply make a function like this in your previous activity make sure to bind it.

    changeData(){
    var mydata= salesmanActions.retrieveAllSalesman();
    this.setState({dataListFill: mydata});
    alert('' + mydata.length);
    }

Simple, then in constructor bind this,

    this.changeData= this.changeData.bind(this);

After that, as I am using react native navigation, so I will simply pass this function to the second screen just like the code below:

    onPress={() => this.props.navigation.navigate('Add Salesman', {doChange: 
    this.changeData} )}

So when the new screen registered as "Add Salesman" will be called, a parameter named "doChange" which is assigned a function will also be transfered to other screen. Now, in other screen call this method anywhere, by :

    this.props.route.params.doChange();

It works for me. I hope works for you too, THANKS for the idea @Bat.

Mehdi Raza
  • 313
  • 3
  • 15
0
 let we have 2 screen A and B , screen A showing all data . and screen B is responsible for adding that data. we add some data on using screen B and want to show instant changes on Screen A . we use below code in A   

    componentDidMount(){
                this.focusListener = this.props.navigation.addListener('focus', () => {
                thi`enter code here`s.startData();
                //Put your Data loading function here
            }); 
    }
0

This is what you can do with react navigation v6. Create a separate stack in stack navigator like this:

const PropertyListStack = () => {
  return (
    <Stack.Navigator screenOptions={{headerShown: false}}>
      <Stack.Screen name={ROUTE_PROPERTY_LIST} component={PropertyList}/>
    </Stack.Navigator>
)};

Now, whenever you you want to reload your initial screen navigate using this stack. like this:

navigation.navigate(
  ROUTE_DASHBOARD_TABS, 
  {screen: ROUTE_PROPERTY_LIST_STACK}
);

This will reload your base screen. In my case base screen is PropertyList.

Sikander Bakht
  • 239
  • 2
  • 8
0

If you know the name of the Screen you want to go , then you can use this code.

navigation.navigate("Screen"); navigation.replace("Screen");

This code works fine if you don't have nested routes.

Ligget
  • 1
  • 1
-2

This answer assumes that the react-native-navigation library is being used, which is unlikely because it doesn't actually have a goBack() method...

The constructor doesn't call a second time because screen A and B are still rendered (but hidden behind screen C). If you need to know when screen B is going to be visible again you can listen to navigation events.

class ScreenB extends Component {
  constructor(props) {
    super(props);
    // Listen to all events for screen B
    this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent);
  }

  onNavigatorEvent = event => {
    switch (event.id) {
      case 'willAppear':
        // refresh your state...
        break;
  };
}

Other events: willDisappear, didAppear, didDisappear

An alternate solution to your problem is to use a state management solution like Redux to provide the state to all screens whenever it is updated (rather than just on screen transitions. See old react-native-nav/redux example.