Search code examples
javascriptreact-nativepromise

React-Native — Building a dynamic URL for API requests using Promise


I am breaking down a larger post into smaller questions. Please understand I never used Promise before and that I am new to React-Native too. It would be great to get feedback and recommendations on how to setup API calls and handle the data.

How can I dynamically create URLs for API requests? Here's what I am trying to achieve:

Pseudocode

Child

  • Retrieve two variables
  • Use these two variables to build an URL
  • Trigger the first Promise and resolve
  • Retrieve another two variables
  • Use these two variables to build a new an URL
  • Trigger the second Promise and resolve
  • Gather the data from both promises and pass to parent

Parent

  • Retrieve data from Child
  • Get data from the first Promise and set to a state
  • Get data from the second Promise and set to another state

APIservice.js

Child

class APIservice {


    _getStopPoint = (endpoint) => {
        return new Promise(function(resolve, reject) {
            fetch(endpoint)
            .then((response) => response.json())
            .then((data) => {
                console.log("APIservice StopPoint", data)
                resolve(data);
            });
        });
    };
};


module.exports = new APIservice

List.js

Parent

As you can see, the way I setup the endpoint is lame. It's not ideal as the URL is the same. I want to structure something that can receive two variables and build the URL on the go. Something like https://api.tfl.gov.uk/Line/${routeid}/Arrivals/${stationid}.

If I manage that, how can I pass the API call to the APIservice having only one endpoint that dynamically will change based on the two variables it receives? I am not sure how to differentiate the call in the Promise.all having only "one" URL.

let APIservice = require('./APIservice')

let endpoint = 'https://api.tfl.gov.uk/Line/55/Arrivals/490004936E'
let endpoint1 = 'https://api.tfl.gov.uk/Line/Northern/Arrivals/940GZZLUODS'

export class List extends Component {
    constructor(props) {
        super(props);

        this.state = {
            bus: null,
            tube: null,
        }
    };

    componentWillMount() {
        let loadData = (endPoint) => {

            Promise.all([
                APIservice._getStopPoint(endpoint),
                APIservice._getStopPoint(endpoint1),
            ])
            .then((data) => {

                // Name for better identification
                const listBus = data[0]
                const listTube = data[1]

                this.setState({
                    bus: listBus,
                    tube: listTube
                }, () => {
                    console.log("bus", this.state.bus, "tube", this.state.tube)
                });
            })
            .catch((error) => {
                console.log(error)
            })
        }

        loadData(endpoint);
        loadData(endpoint1);

    }

    render() {
        return(
            <View>
                <FlatList 
                data={this.state.bus}
                renderItem={({item}) => (
                    <Text>{item.timeToStation}</ Text>
                )}
                keyExtractor={item => item.id}
                />
                <FlatList 
                data={this.state.tube}
                renderItem={({item}) => (
                    <Text>{item.timeToStation}</ Text>
                )}
                keyExtractor={item => item.id}
                />
            </ View>
        );
    }
};

Solution

  • It is pretty easy to implement what you are saying once you understand how this works.

    You are using fetch for your API calls which returns a Promise upon use. The pseudo-code for your use case would be something like this:

    class APIService {
        static fetchFirst(cb) {
            fetch('FIRST_URL')
                .then(resp => {
                    try {
                        resp = JSON.parse(resp._bodyText);
                        cb(resp);
                    } catch(e) {
                        cb(e);
                    }
                })
                .catch(e => cb(e));
        }
    
        static fetchSecond(routeid, stationid, cb) {
            fetch(`https://api.tfl.gov.uk/Line/${routeid}/Arrivals/${stationid}`)
                .then(resp => {
                    try {
                        resp = JSON.parse(resp._bodyText);
                        cb(resp);
                    } catch(e) {
                        cb(e);
                    }
                })
                .catch(e => cb(e));
        }
    }
    
    module.exports = APIService;
    

    Include this in your parent component and use it as follows:

    let APIService = require('./APIService')
    
    export class List extends Component {
        constructor(props) {
            super(props);
    
            this.state = {
                bus: null,
                tube: null,
            }
        };
    
        componentWillMount() {
            APIService.fetchFirst((resp1) => {
                APIService.fetchSecond(resp1.routeid, resp1.stationid, (resp2) => {
                    this.setState({
                        tube: resp2
                    });
                });
            });
        }
    
        render() {
            return(
                <View>
                    <FlatList 
                    data={this.state.bus}
                    renderItem={({item}) => (
                        <Text>{item.timeToStation}</ Text>
                    )}
                    keyExtractor={item => item.id}
                    />
                    <FlatList 
                    data={this.state.tube}
                    renderItem={({item}) => (
                        <Text>{item.timeToStation}</ Text>
                    )}
                    keyExtractor={item => item.id}
                    />
                </ View>
            );
        }
    };
    

    I haven't checked the errors on the callback function, please see that the errors are handled when you use this.