reactjsreact-nativeaxioszomato-api

Creating Search function with React Native for Zomato API


I am new to using APIs with React Native, I have a few things figured out but what I'm trying to do now is create a Search function. I've tried a few things so far, but either the function isn't being called, or I get an error about the function being called as an object. Also, I am using axios to make this API call

Here's what I've got so far.

export default class CategoryScreen extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            data: [],
            isVisible: true,
            city: '280',
            isLoading: true,
            search: ''
        }
    }

    async componentDidMount() {
        try {
            const id = this.props.navigation.state.params.category;
            const city = this.state.city;

            const result = await axios.request({
                method: 'get',
                url: `https://developers.zomato.com/api/v2.1/search?city_id=${city}&category=${id}`,
                headers: {
                    'Content-Type': 'application/json',
                    'user-key': 'a31bd76da32396a27b6906bf0ca707a2'
                }
            });
            this.setState({ data: result.data.restaurants });
        } catch (err) {
            console.log(err);
        } finally {
            this.setState({ isLoading: false });
        }
    }

    updateSearch = search => {
        this.setState({ search });
    };

    searchReq = () => {
        axios.request({
            method: 'get',
            url: `https://developers.zomato.com/api/v2.1/search?city_id=${city}&q${search}&category=${id}`,
            header: {
                'Content-Type': 'application/json',
                'user-key': 'a31bd76da32396a27b6906bf0ca707a2'
            }
        });
        this.setState({ data: result.data.restaurants });
    }

    render() {
        const { search } = this.state;
        return (
            <SearchBar
                placeholder="Type Here..."
                onChangeText={this.updateSearch}
                onSubmitEditing={this.searchReq}
                value={search}
            />
        )
    }
}

If there is a more advanced way of doing it I'm open to learning something new, I just want to accomplish what I want to do. Also, I know that if my current method worked it'd still cause problems if the user adds a space in between, so I'm very open to learning new and more advanced ways of doing this

Here is the expo snack https://snack.expo.io/SJoIr8u1I


Solution

  • I noted couple of mistake of your search function.

    1. You need to get city, search & id value which are not in searchReq()
    2. You need to get result.data.restaurants value before you assign it to data: result.data.restaurants

    Since you already assign this.setState({ search }) I get that value inside searchReq() as below,

    searchReq = async () => {
        try {
            const result = await axios.request({
                method: 'get',
                url: `https://developers.zomato.com/api/v2.1/search?city_id=280&q${this.state.search}&category=11`,
                headers: {
                    'Content-Type': 'application/json',
                    'user-key': 'a31bd76da32396a27b6906bf0ca707a2'
                }
            });
            this.setState({ data: result.data.restaurants });
        } catch (error) {
            this.setState({ data: [] });
            console.log(error);
        }
    }
    

    So full source code will be like this

    import React, { Component } from 'react';
    import {
        View,
        Text,
        FlatList,
        StyleSheet,
        TouchableOpacity,
        Dimensions,
        ActivityIndicator,
    } from 'react-native';
    import { Card, Image, SearchBar, Icon } from 'react-native-elements';
    import Logo from '../assets/Logo.png'
    import axios from 'axios';
    
    const SCREEN_WIDTH = Dimensions.get('window').width;
    const SCREEN_HEIGHT = Dimensions.get('window').height;
    
    export default class CategoryScreen extends Component {
    
        constructor(props) {
            super(props);
            this.state = {
                data: [],
                isVisible: true,
                city: '280',
                isLoading: true,
                search: ''
            }
        }
    
        async componentDidMount() {
            try {
                const result = await axios.request({
                    method: 'get',
                    url: `https://developers.zomato.com/api/v2.1/search?city_id=280&category=11`,
                    headers: {
                        'Content-Type': 'application/json',
                        'user-key': 'a31bd76da32396a27b6906bf0ca707a2'
                    }
                });
                this.setState({
                    data: result.data.restaurants,
                    isLoading: false
                });
            } catch (err) {
                this.setState({ isLoading: false });
                console.log(err);
            }
        }
    
        updateSearch = search => {
            this.setState({ search });
        };
    
        searchReq = async () => {
            try {
                const result = await axios.request({
                    method: 'get',
                    url: `https://developers.zomato.com/api/v2.1/search?city_id=280&q${this.state.search}&category=11`,
                    headers: {
                        'Content-Type': 'application/json',
                        'user-key': 'a31bd76da32396a27b6906bf0ca707a2'
                    }
                });
                this.setState({ data: result.data.restaurants });
            } catch (error) {
                this.setState({ data: [] });
                console.log(error);
            }
        }
    
        render() {
            const { search } = this.state;
            return (
                <View>
                    <View style={styles.header}>
                        <Image source={Logo} style={{ resizeMode: 'stretch', width: 50, height: 50, alignSelf: 'center', justifyContent: 'center', marginTop: 30 }} />
                        <Icon
                            raised
                            name='location'
                            type='octicon'
                            color='#f50'
                            onPress={() => this.setState({ isModalVisible: !this.state.isModalVisible })}
                            containerStyle={{ alignSelf: 'flex-end', marginRight: 20, marginTop: -60 }} />
                    </View>
                    <SearchBar
                        placeholder="Type Here..."
                        onChangeText={this.updateSearch}
                        onSubmitEditing={this.searchReq}
                        value={search}
                        inputStyle={{ backgroundColor: 'steelblue', color: 'white' }}
                        inputContainerStyle={{ backgroundColor: 'steelblue', fontColor: 'white' }}
                        containerStyle={{ backgroundColor: 'steelblue', borderBottomColor: 'transparent', borderTopColor: 'transparent' }}
                        placeholderTextColor='white'
                    />
                    {
                        this.state.isLoading ?
                            <View style={{ flex: 1, marginTop: 200 }}>
                                <ActivityIndicator style={{ color: 'red' }} />
                            </View> :
                            (
                                this.state.data.length == 0 ?
                                    <View style={{ flex: 1, padding: 20, marginTop: 100 }}>
                                        <Text style={{ color: '#000', fontWeight: 'bold' }}>No restaurants from selected category</Text>
                                    </View> :
                                    <View style={{ flexDirection: 'column' }}>
                                        <FlatList
                                            keyExtractor={item => item.id}
                                            data={this.state.data}
                                            renderItem={({ item }) =>
                                                <TouchableOpacity onPress={() => console.log(item.restaurant.location.city)}>
                                                    <Card style={{ width: SCREEN_WIDTH }}>
                                                        <Image style={{ width: SCREEN_WIDTH / 3, height: 130 }} resizeMode="stretch" source={{ uri: item.restaurant.thumb }} />
                                                        <Text style={{ color: '#000', fontWeight: 'bold' }}>{item.restaurant.name} </Text>
                                                    </Card>
                                                </TouchableOpacity>
                                            }
                                        />
                                    </View>
                            )
                    }
                </View>
            )
        }
    }
    
    const styles = StyleSheet.create({
        header: {
            backgroundColor: '#ff4714',
            height: SCREEN_HEIGHT / 8,
    
        },
    });
    

    Hope this helps you.