react-nativeexpouse-effectreact-native-button

How to do a useEffect() style ajax call after button press in React Native?


I'm new to React and React Native. Trying to build a simple app that does an API call on the press of a button.

My code below works but just doesn't "look right" to me. useEffect() is run after the page loads? Seems to be run multiple times. I'm counting button clicks as true and then have an if statement inside useEffect. Does that mean the app is constantly checking if the button is pressed?

I tried putting useEffect() inside the getJoke() function but react native didn't like it. "Invalid hook call. Hooks can only be called inside of the body of a function component."

If I put getJoke() inside useEffect() it calls the api constantly. I jet api response after response after response.

I just want one button press to result in one API call, and the text below the button to be updated like in AJAX if this was a webpage.

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

export default function App() {

 const [isLoading, setLoading] = useState(true);
 const [data, setData] = useState();
 const [ error, setError ] = useState(false);
 const [buttonPress, setButtonPress] = useState(false);

 const getJoke = async () => {
  try {
   const response = await fetch('https://api.chucknorris.io/jokes/random');
   const json = await response.json();
   console.log(json);
   console.log('Joke: ' + json.value);
   setData(json.value);   // Chuck API provides joke in "value" field. Only saving the joke.

  // Unfortunately you can't call hooks useEffect from here?

 } catch (error) {
   console.error(error);
   setError(true);       
 } finally {
   setLoading(false);
 }
}

// Constant api calls if I do this
 /*
 useEffect(() => {
  getJoke();
 });
 */

 useEffect(() => {
   // Check if button has been pressed, if so update the page
   if(buttonPress){
    getJoke();
    setButtonPress(false);
   }
 });

return (
  <View style={{ flex: 1, padding: 24 }}>
   <Text>Press button to get a random joke:</Text>
   <Button title="Joke" onPress={setButtonPress}/>
  
   <Text>{data}</Text>
  
  </View>
);
}

Solution

  • runs after every rendering

      useEffect(() => {
        // Runs after EVERY rendering
      });
    

    runs once after the initial rendering.

      useEffect(() => {
        // Runs ONCE after initial rendering
      }, []);
    

    runs only when any dependency value changes.

      useEffect(() => {
        // Runs ONCE after initial rendering
        // and after every rendering ONLY IF `prop` or `state` changes
      }, [prop, state]);
    

    So, I think you should do like this

    useEffect(() => {
       // Check if button has been pressed, if so update the page
       if(buttonPress){
        getJoke();
        setButtonPress(false);
       }
    }, [buttonPress]);
    

    And the reason for the Invalid hook call is there are few rules.

    Don’t call Hooks inside loops, conditions, or nested functions