Hearner
Hearner

Reputation: 2729

Use static fetch service

I have created an express mongoose api. I want to use that api from my React-application.

I want to create a service that would manage those api requests. But I am new in react-native and I can't use that service. I tried creating a static class but I cannot make it works. Here is an example :

// apiService.js
class ApiService {
    static fetchUsers = () => {
        return fetch('XXX/users')
            .then((response) => {
                return response.json()
                    .then((data) => {
                        return data;
                    })
            })
            .catch((error) => {
                console.error(error);
            });
    }
}

export default ApiService;

And my screen

// UserScreen.js
import ApiService from '../services/apiService';

export default class UserScreen extends Component {
     constructor(props) {
        super(props);

        this.state = {
            users: [],
            isLoading: true,
        }
    }

    componentDidMount = () => {
       let users = ApiService.fetchUsers();
       this.setState({data: users});
       this.setState({isLoading: false});
    }

    render() {
        if (this.state.isLoading) {
            return (
                <View style={styles.container}>
                    <ActivityIndicator/>
                </View>
            )
        } else {
            return (
                <View style={{ flex: 1, marginTop: 100 }}>
                    {
                        this.state.data.map((val, key) => {
                            return <TouchableOpacity
                                style={styles.homeButton}
                                key={key}
                                onPress={() => this.redirectHandler(val)}>
                            </TouchableOpacity>
                        })
                    }
                </View>
            )
        }
    }


}

I tried using async and wait but I can't find a way to retrieve data. The data are well retrieve in the apiService but I can't share them with the UserScreen.

How can I use a (static or not) class/function in react-native and get the data from the screen

Update Here is what I tried with async

class ApiService {
    static fetchUsers = async () => {
        try {
            let response = await fetch('XXXXX/users/');
            let json = await response.json();
            return json;
        } catch (error) {
            console.error(error);
        }
    }
}

export default ApiService;

And in my Userscreen

componentDidMount = async () => {
        try {
            let users = await ApiService.fetchUsers();
            this.setState({isLoading: false});
            this.setState({data: users});
        } catch (error) {
            console.log(error);
        }
    }

Upvotes: 0

Views: 878

Answers (1)

Ajay Gupta
Ajay Gupta

Reputation: 2033

The problem lies in the setState that you are performing twice. If you look at the logic of the component, first we check for isLoading, if true we show some message/spinner otherwise we are showing a list of users/data.

Sequence of the Set State:

this.setState({isLoading: false});
this.setState({data: users});

Note that each setState triggers a re-render of the component, so in this case first we set isLoading to false (1st Re-Render) and then we set the data (2nd Re-Render)

The problem is, when 1st Re-Render is done, isLoading is set to false and the condition which we talked about above, now enters the "showing the user/data list" part. Another thing to note here is we have defined users: [] in state and when we are setting the users array (from the api call), we set that in a variable called data. The variable data is never defined in state so essentially it is "undefined".

Issue in your code:

this.state.data.map(...)

You cannot map over an undefined variable and this is where the problem lies. Doing so will throw an error saying "cannot read property map of undefined".

To fix this:

When setting the users list, instead of doing this.setState({ data: users }) just do this.setState({ users: users }) and change this.state.data.map( to users.map(

Also, unnecessary re-renders are costly and in case of React Native, they are costlier. Merge your setState(...) calls when possible. For example,

this.setState({
    isLoading: false,
    users: users
})

Upvotes: 1

Related Questions