Home > Software design >  Error querying the api: This works in the web browser but not on mobile. (expo)
Error querying the api: This works in the web browser but not on mobile. (expo)

Time:03-03

I'm new to react native and I'm not able to consume this api, when I start this app in the browser, it works fine, but when I go to the expo app it doesn't display the pokemon image, could someone help me?

import { StatusBar } from 'expo-status-bar';
import React, { useEffect, useState } from 'react';
import { StyleSheet, Text, View, Button, Alert, TextInput, Image } from 'react-native';

interface PokeInterface {
  sprites : {
    back_default : string;
  }
}
export default function App() {
  const [text, setText] = useState<string>("")
  const [response, setResponse] = useState<PokeInterface | any>()
  const [image, setImage] = useState<string>()
  const handleText = (text : string) => {
    setText(text)
  }

  const searchApi = (pokemonName : string) => {
    fetch(`https://pokeapi.co/api/v2/pokemon/${pokemonName}/`, { method: 'GET'})
      .then((response) => response.json())
        .then((response) => setResponse(response))

        
  }
  useEffect(() => {
    if(text){
      searchApi(text)
    }
    if(response){
      const {sprites} = response
      setImage(sprites.front_default)
    }

    return () => {
      if(image){
        setImage("")
      }
    }
  },[text, response])

  return (
    <View style={styles.container}>
      <View style={styles.topbar}>
        <Text style={styles.title}>Pokedex Mobile</Text>
        <TextInput
          style={styles.input}
          onChangeText={(value: any) => handleText(value)}
          value={text}
          placeholder="Search Pokemon"
          keyboardType="default"
        />
        <Text style={styles.text}>{text}</Text>
      </View>
      {image && (
        <Image 
          style={styles.logo} 
          source={{uri : `${image}`}}
          />
      )}
    </View>
  );
}

const styles = StyleSheet.create({
  text : {
    fontSize: 30,
    color : "red"
  },
  input: {
    height: 40,
    margin: 12,
    borderWidth: 1,
    padding: 10,
  },
  container : {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center'
  },
  title: {
    fontSize: 30,
    color: '#000'
  },
  topbar: {
  },
  logo : {
    width: 200,
    height: 200
  }
});

CodePudding user response:

Your current code causes an infinite loop...

  1. You type some text which triggers searchApi(text)
  2. That writes to response which triggers your effect hook
  3. Because text is still truthy, it triggers searchApi(text) again
  4. Goto #2

As far as I can tell, you can simply discard most of response and just retrieve the image when the text changes.

// this doesn't need to be defined in your component
const getPokemonImage = async (name: string) => {
  const res = await fetch(
    `https://pokeapi.co/api/v2/pokemon/${encodeURIComponent(pokemonName)}/`
  );

  if (!res.ok) {
    throw new Error(`${res.status}: ${await res.text()}`);
  }

  return (await res.json<PokeInterface>()).sprites.front_default;
};

export default function App() {
  const [text, setText] = useState<string>("");
  const [image, setImage] = useState<string>(""); // initial value

  const handleText = (text: string) => {
    setText(text);
  };

  useEffect(() => {
    if (text) {
      getPokemonImage(text).then(setImage);
    }
  }, [ text ]); // only run on text change

CodePudding user response:

import React, {useEffect, useState} from 'react';
import {StyleSheet, Text, View, TextInput, Image, Button} from 'react-native';

// Sorry for removing the types i was using JS
// This code works test it for yourself
export default function App() {
  const [text, setText] = useState('');
  const [response, setResponse] = useState(); // Here use PokeInterface || any as you are doing conditional checking
  const [image, setImage] = useState();
  const handleText = e => {
    setText(e);
  };

  const searchApi = pokemonName => {
    fetch(`https://pokeapi.co/api/v2/pokemon/${pokemonName}/`, {method: 'GET'})
      .then(response => response.json())
      .then(response => setResponse(response));
  };
  useEffect(() => {
    if (response) {
      const {sprites} = response;
      console.log(response);
      setImage(sprites.front_default);
    }
    return () => {
      if (image) {
        setImage('');
      }
    };
  }, [image, text, response]); // you have to pass all the dependencies so useEffect will be invoked as you didnt pass image dep the useeffct was not invoking

  return (
    <View style={styles.container}>
      <View style={styles.topbar}>
        <Text style={styles.title}>Pokedex Mobile</Text>
        <TextInput
          style={styles.input}
          onChangeText={(value: any) => handleText(value)}
          value={text}
          placeholder="Search Pokemon"
          keyboardType="default"
        />
        <Text style={styles.text}>{text}</Text>
      </View>
      {image && <Image style={styles.logo} source={{uri: `${image}`}} />}
      <Button
        title={'Submit'}
        onPress={() => searchApi(text)}
        disabled={!text}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  text: {
    fontSize: 30,
    color: 'red',
  },
  input: {
    height: 40,
    margin: 12,
    borderWidth: 1,
    padding: 10,
  },
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
  title: {
    fontSize: 30,
    color: '#000',
  },
  topbar: {},
  logo: {
    width: 200,
    height: 200,
  },
});
  • Related