3

EDIT: The async calls have now been placed inside an axios.all() call to speed up the process. The problem remains the same.

In my app, when pressing log out on the Login screen, a token that is stored on the device is deleted.

Login.js:

    async resetKey() {
        try {
            await AsyncStorage.removeItem('@MySuperStore:key');
            this.setState({
                token: null,
                loggedIn: false,
            });
        } catch (error) {
            this.setState({
                token: null,
                loggedIn: false,
            });
        }
    }

Now I want to use this fact in another page, where I base the render on a prop loggedIn. If logged in, so true, I'm rendering activities and either a green or red box indicating whether the user is registered for the activity. This is all fairly trivial, and works fine :

    if (this.state.loggedIn) {
        return (
            <ScrollView refreshControl={
                <RefreshControl refreshing={this.state.refreshing} onRefresh={this.onRefresh.bind(this)} />
            }>
                <Loader loading={this.state.loading} />

                <Text style={styles.foodHeader}>Activiteiten</Text>
                {this.state.activities.map((activity) => (
                    <TouchableOpacity key={activity.id} onPress={() => this.onOpenActivity(activity)} style={styles.foodActivityField}>
                        <Text>{`${activity.name}`}</Text>
                        <View>
                            {this.showRegistered(activity.id) ? registered : notRegistered}
                        </View>
                    </TouchableOpacity>
                ))}

                <Text style={styles.foodHeader}>Eten</Text>
                {this.state.foods.map((food) => (
                    <TouchableOpacity key={food.id} onPress={() => this.onOpenFood(food)} style={styles.foodActivityField}>
                        <Text>{`${food.name}`}</Text>
                        <View>
                            {this.showRegistered(food.id) ? registered : notRegistered}
                        </View>
                    </TouchableOpacity>
                ))}

            </ScrollView>
        );
    }

However, this should not be displayed when a user has just logged out. In this case it should show a simple text saying 'You have to be logged in to view activities'.

    else {
        {/** User is not logged in */ }
        return (
            <ScrollView refreshControl={
                <RefreshControl refreshing={this.state.refreshing} onRefresh={this.onRefresh.bind(this)}  />
            }>
                <Loader loading={this.state.loading} />
                <Text>You have to be logged in to view activities</Text>
            </ScrollView>
        );
    }

The rendering of registered, notRegistered, and the onOpenFood and onOpenActivity functions have been left out for simplicity. For the functionality of the refresh, and the API calls to work I have this piece of code (The functions of the API calls are in seperate files, these are basic axios requests, both GET and POST) :

constructor(props) {
    super(props);
    this.state = {
        loading: true,
        refreshing: false,
        activities: [],
        foods: [],
        registered: []
    }
    this.getData
}

/* Handles the action necessary to refresh */
onRefresh = () => {
    this.setState({ refreshing: true });
    this.getData().then(() => {
        this.setState({ refreshing: false });
    });
}

async componentDidMount() {
    // Do the necessary API calls
    this.getData();
}

getData = async () => {

    this.setState({loading: true})
    /* Get token from storage */
    try {
        console.log('trying to get key...')
        const value = await AsyncStorage.getItem('@MySuperStore:key');
        if (value) {
            this.setState({
                token: value,
                loggedIn: true
            });

            /* Retrieve activities */
            const activities = await getActivities();
            this.setState({ activities });

            /* Retrieve food */
            const foods = await getFood();
            this.setState({ foods });

            /* Retrieve the activity ID's the user is registered for */
            const registered = await getRegistered(this.state.token);
            this.setState({ registered });
            this.setState({ loading: false })

        } else {
            this.setState({
                token: null,
                loggedIn: false,
                loading: false
            });
        }
    } catch (error) {
        this.setState({
            token: null,
            loggedIn: false
        });
        console.log("help")
    }


}

Now the problem is in the fact that the this.state.registered change is not seen at the reopening of the page, leading to an incorrect display of the page. Refreshing the page works, since the entire states are reset. How can this be fixed? Is there a way to for instance call onRefresh upon loading the page again? The page, as well as the pages the functions onOpenFood and onOpenActivity navigate to, are part of a StackNavigator, which itself is part of a DrawerNavigator (react-navigation 3.3.2).

1 Answers1

3

In case anyone stumbles upon this problem, you might want to look at something like a useEffect hook combined with a listener. In this case that would look something like this;

useEffect(()=> {
  const unsubscribe = navigation.addListener('focus', () => {
    onRefresh();
  });

  return unsubscribe;
}, [navigation]);

This makes sure to call the onRefresh function whenever the focus on the page comes back and is done whenever something in the navigation prop has changed. This is an old problem so I have not adapted this in the code, but in newer projects, this seems to work just fine