0

I am trying to update state to show an ActivityIndicator while an asynchronous call is being made.

What is the correct way of doing this to ensure IsLoading(true) and setResult(null) will happen before the the async call?

In the code below setIsLoading is not actually updated before the async call and thus the ActivityIndicator does not appear on the UI. I realize why this is the case (here) I just don't know the recommended way to do actually ensure the ActivityIndicator shows before the async call is made.

Please help. What is the correct way to do this in react?

const MyScreen = () => {
    const [isLoading, setIsLoading] = useState(false);
    const [result, setResult] = useState(null);

    const handlePress = async () => {

        setResult(null);  // reset result
        setIsLoading(true);  // show activity indicator

        const result = await MyAsyncFunc();  // native modules function
        setResult(result);
        setIsLoading(false);  // turn off activity indicator

    }

    return (
        <View style={styles.container}>
            <MyButton title="Run" handlePress={handlePress} disabled={isLoading} />
            {isLoading && <ActivityIndicator size="large" color="#00ff00" />}
            <Text>{result}</Text>
        </View>
    )
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center'
    },
})
gavinest
  • 308
  • 2
  • 12

2 Answers2

1

Can you try using just one state? no need isLoading for this I think. try this

{!result && <ActivityIndicator size="large" color="#00ff00" />}

EDIT: lets utilize useEffect to listen to if isLoading is true

...
useEffect(() => {
 async function load (){
  if (isLoading) {
   const result = await MyAsyncFunc();  // native modules function
   setResult(result);
   setIsLoading(false);  // turn off activity indicator
  }
 }
 load()
}, [isLoading])

const handlePress = () => {
   setResult(null);  // reset result
   setIsLoading(true);  // show activity indicator
}
idiglove
  • 325
  • 3
  • 10
  • When the user first visits the screen `result` will be null and I would **not** like the ActivityIndicator showing. They'll press the button to trigger the async function. – gavinest Dec 22 '21 at 01:49
  • Thank you for the edit. That works! And I learned something :D – gavinest Dec 22 '21 at 13:57
0

Try adding useEffect and set isLoading false on changing of result dependency there, like this:

const MyScreen = () => {
    const [isLoading, setIsLoading] = useState(false);
    const [result, setResult] = useState(null);

    const handlePress = async () => {

        setResult(null);  // reset result
        setIsLoading(true);  // show activity indicator

        const result = await MyAsyncFunc();  // native modules function
        setResult(result);
        //setIsLoading(false);  // turn off activity indicator

    }

    useEffect(() => {
        if(result && isLoading){
            setIsLoading(false);
        }
    }, [isLoading, result])

    return (
        <View style={styles.container}>
            <MyButton title="Run" handlePress={handlePress} disabled={isLoading} />
            {isLoading && <ActivityIndicator size="large" color="#00ff00" />}
            <Text>{result}</Text>
        </View>
    )
}

Hope this works for you

Shoaib Khan
  • 1,020
  • 1
  • 5
  • 18
  • Thank you for the response but I see the same issue when I run this code. I think idiglove's answer above solves the issue by putting `MyAsyncFunc` in a `useEffect that listens to changes on `isLoading`. This guarantees that `isLoading` will change before `MyAsyncFunc` runs. – gavinest Dec 22 '21 at 14:03