简体   繁体   中英

React Native: How to prevent state reset when changing screens?

I am having trouble when changing screens after the user access a card. The component set of states resets whenever the client accesses a card, changes the screen, and returns to the same component before.

Here is how the app works. The client access a list of quotes (QuotesRequestedListScreen) that they requested. This said list is loaded from Firebase, and thus the state to indicate loading activity is set to true at first. After the load finishes, then the app shows all the data in cards. After the client clicks on the card, it changes the screen and displays the card info. However, if the client returns to the previous screen, the screen shows the loading activity icon forever until changing screens again.

I have tried to use State Persistance given by React Navigation , but it did not work when I was trying to return to the quote list screen (QuotesRequestedListScreen).

Does someone know what is the best way to keep the states when changing screens? Thanks!

Main Quote Screen

const QuoteScreen = (props) => {
  
  const QuotesRequestedScreen = () => {
    return (
      <Stack.Navigator headerMode="none" initialRouteName="Quote List">
        <Stack.Screen name="Quote List" component={QuotesRequestedListScreen} />
        <Stack.Screen name="Card" component={QuoteRequestedCardScreen} />
      </Stack.Navigator>
    );
  };

  return (
    <Tab.Navigator initialRouteName="Request Quote">
      <Tab.Screen name="Request Quote" component={RequestQuoteScreen} />
      <Tab.Screen name="Quotes Requested" component={QuotesRequestedScreen} />
    </Tab.Navigator>
  );
};

export default QuoteScreen;

QuotesRequestedListScreen

const QuotesRequestedListScreen = (props) => {
  //Getting userID for the useEffect
  let userId = useSelector((state) => state.user.userId);
  const {height} = Dimensions.get('window');

  //
  ////USESTATES
  //

  const [quoteStatusCards, setQuoteStatusCards] = useState([]);
  const [airportList, setAirportList] = useState([]);
  const [rawQuotes, setRawQuotes] = useState([]);
  const [errorDetector, setErrorDetector] = useState(false);
  const [isLoading, setIsLoading] = useState(true);
  const [isUserLogged, setIsUserLogged] = useState(true);

  //
  ////USEEFFECTS
  //

  useEffect(() => {
    //Function that starts the verification once it has the focus
    const unsubscribe = props.navigation.addListener('focus', () => {

    if (userId !== null) {
        console.log('Entrei aqui');
        getQuotes();
    } else {
        setIsLoading(false);
        setIsUserLogged(false);
    }
    });

    return unsubscribe;
  }, [userId]);

  //
  //// FUNCTIONS
  //

  const getQuotes = async () => {
    await firebase.getQuotesById(userId).then(async (results) => {
      if (results.isError) {
        errorOperationQuote(results.errorMessage);
      } else {
        //Then it tries to get the airportlist from asyncStorage.
        await AsyncStorage.getItem('airportList')
          .then(async (list) => {
            //Checks if the list is empty
            if (list != null) {
              //If it's not, then it checks the last version in the database with the one stored in the asyncstorage
              await firebase.compareAirportListVersion().then((result) => {
                if (result.isError) {
                  //If there are errors, then it reports the error to the user
                  errorOperationQuote(result.errorMessage);
                } else {
                  if (result.success) {
                    //If it's the same version, then it loads inside airportList;
                    setAirportList(JSON.parse(list));
                    setRawQuotes(results.quotes);
                    setIsLoading(false);
                  } else {
                    //If it's not the same version, then it downloads the whole list again.
                    downloadAirportList(results.quotes);
                  }
                }
              });
            } else {
              //If the list's empty, then it downloads the airport
              downloadAirportList(results.quotes);
            }
          })
          .catch((error) => {
            errorOperationQuote(error.errorMessage);
          });
      }
    });
  };

  //Downloads the airport list and set it to the AirportList state.
  const downloadAirportList = async (quotes) => {
    await firebase.getAirports().then((result) => {
      if (result.success) {
        setAirportList(JSON.parse(result.airportList));
        setRawQuotes(quotes);
      } else {
        errorOperationQuote(result.errorMessage);
      }
    });
  };

  //
  ////RETURN
  //

  return (
    <Container>
      <Content contentContainerStyle={styles.quoteCardsContainer}>
        {isLoading ? (
          <View style={styles.loadingErrorContainers}>
            <Spinner style={{alignItems: 'center'}} color={colors.colorRed} />
          </View>
        ) : !isUserLogged ? (
          <View style={styles.loadingErrorContainers}>
            <Text style={styles.errorMessageText}>
              User is not logged in. Please, log in first before checking the
              quotes requested.
            </Text>
            <Button
              onPress={() => {
                props.navigation.navigate('Login');
              }}>
              Login
            </Button>
          </View>
        ) 
///...
      </Content>
    </Container>
  );

}

QuoteRequestedCardScreen

const QuoteRequestedCardScreen = (props) => {
  const [quoteInfo, setQuoteInfo] = useState(props.route.params?.selectedQuote);
  console.log(quoteInfo);

  return (
    <Container>
      <Content style={{marginHorizontal: 10}}>
        <Card>
          <CardItem style={{paddingLeft: 0}} header>
            <View style={{flexDirection: 'row'}}>
              <Button
                onPress={() => props.navigation.navigate('Quote List')}
                transparent>
                <Icon name="arrow-left" type="FontAwesome5" />
              </Button>
              //...
            </View>
          </CardItem>
        </Card>
      </Content>
    </Container>
  );
};

export default QuoteRequestedCardScreen;

The problem is that you're creating a component inside another component. Your function component QuotesRequestedScreen is defined inside the QuoteScreen function.

You should NEVER define components inside other components or your state will be lost on re-render due to remount. Just move the QuotesRequestedScreen to outside the QuoteScreen function and then it'll work as expected.

More information from React Navigation docs https://reactnavigation.org/docs/troubleshooting#screens-are-unmountingremounting-during-navigation

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM