firebase realtime database is not getting fetched with onPress in react native, have to refresh the ref().on function everytime - javascript

I have a realtime database with main node 'user' and then inside it i have 3 child nodes and those 3 child nodes have 4 more child nodes, each of them. One of the 4 nodes is a recording, one is image and 2 of them are strings. I am trying to fetch them dynamically with Next and Back button where on pressing next, next node's data is displayed on screen.
I am using a useState for dynamically changing the path of database (ref), but on pressing the next/back button, my data on screen does not get updated. Also later I found out that after pressing next/back button when I refresh/rewrite the ref().on function, my data gets updated, but I have to do this for every press.
Here's my App.js code:
import Sound from 'react-native-sound';
import database from '#react-native-firebase/database';
import storage from '#react-native-firebase/storage';
import React , {useEffect, useState} from 'react';
import {
ScrollView,
StyleSheet,
Alert,
Text,
View,
Image,
Button
} from 'react-native';
const App = () => {
const [myData,setData] = useState({
letter:'',
pronun:'',
word:'',
image:''
});
const [img,setimg] = useState(null);
const [pronunn,setpronun] = useState(null);
const [hey,sethey] = useState(1);
useEffect(() => {
getDatabase();
}, []);
function getDatabase() {
database().ref('users/'+hey+'/').on('value' , (snapshot) => {
Sound.setCategory('Playback', true);
var poo=new Sound(snapshot.val().pronun);
setData({
letter: snapshot.val().letter,
word: snapshot.val().word,
image: setimg(snapshot.val().image),
pronun: setpronun(poo)
});
console.log(snapshot.val());
});
}
return (
<View style={{flex:1, backgroundColor:'#000000', alignContent:'center', alignItems:'center', justifyContent:'center'}}>
<ScrollView>
<Text style={{color:'#ffff'}}>
Letter: {myData ? myData.letter : 'loading...' }
</Text>
<Text style={{color:'#ffff'}}>
Word: {myData ? myData.word : 'loading...' }
</Text>
<Image style={{width:200, height:200}}
source={{uri: img}}
/>
<View>
<Button
title='Pronunciation'
onPress={() => {
return pronunn.play();
}}
>
</Button>
<Button title='Next' onPress={
() => {
if (hey>2) {
Alert.alert('no more records');
}
else {
return sethey(hey+1);
}
}
}
>
</Button>
<Button title='back' onPress={
() => {
if (hey<2) {
Alert.alert('no more records to go back');
}
else {
return sethey(hey-1);
}
}
}
>
</Button>
</View>
</ScrollView>
</View>
);
};
export default App;

Since your setData hook/effect depends on the hey state, you need to specify the latter as a dependency in useEffect for the data loading.
useEffect(() => {
getDatabase();
}, [hey]);
Also see:
The documentation on useEffect, specifically the section on dependencies.
setState in React's useEffect dependency array

Related

Why can't I call the parent function passed as props to child component?

What I am trying To Do
I am building a simple expo managed audio player app. On my App Screen, I need display a list of songs. When a user clicks on the song, it plays and once the play finishes, the "Songs Played" at the bottom of the page should increase. I am using expo-av API for this.
Here is the breakdown of the app:
App.js
Here I have an array (Data) that holds the songs. To keep it simple, I am using the same song for all elements. count variable holds the count of songs and there is a function (IncreaseCount) which is passed to the ChildComponent as prop. Flatlist is used to render the ChildComponents
import { View, Text, FlatList } from 'react-native'
import React, {useState} from 'react'
import ChildComponent from './ChildComponent';
const Data = [
{
key: "1",
song: "https://www2.cs.uic.edu/~i101/SoundFiles/CantinaBand3.wav"
},
{
key: "2",
song: "https://www2.cs.uic.edu/~i101/SoundFiles/CantinaBand3.wav"
},
{
key: "3",
song: "https://www2.cs.uic.edu/~i101/SoundFiles/CantinaBand3.wav"
}
]
export default function App() {
const [count, setcount] = useState(0);
const IncreaseCount = ()=>{
setcount(count + 1);
}
const renderItem = ({item, index})=>{
return(
<View style={{marginTop: 10}} >
<ChildComponent path={item.path} IncreaseCount={()=>IncreaseCount} index={index} songURL={item.song}/>
</View>
)
}
return (
<View style={{justifyContent: "center", alignItems: "center", marginTop: 200}}>
<FlatList
data={Data}
renderItem={renderItem}
extraData={count}
/>
<Text style={{marginTop: 30}}> Number of Songs Played: {count} </Text>
</View>
)
}
ChildComponent
Here I use expo-av API. Using the loadAsync() method, I Initially load the songs upon first render using useEffect hook. Then using onPress method of the button I invoke the playAsync() method of the playBackObject.
Using the setOnPlayBackStatusUpdate method, I listen for status changes. When playBackObjectStatus.didJustFinish becomes true, I call the props.IncreaseCount().
import { View, Button } from 'react-native'
import React, {useRef, useEffect} from 'react'
import { Audio } from 'expo-av';
export default function ChildComponent(props) {
const sound = useRef(new Audio.Sound());
const PlayBackStatus = useRef();
useEffect(()=>{
LoadAudio();
return ()=> sound.current.unloadAsync()
},[])
const LoadAudio = async ()=>{
PlayBackStatus.current = sound.current.loadAsync({uri: props.songURL})
.then((res)=>{
console.log(`load result : ${res}`)
})
.catch((err)=>console.log(err))
}
const PlayAuido = async ()=>{
PlayBackStatus.current = sound.current.playAsync()
.then((res)=>console.log(`result of playing: ${res}`))
.catch((err)=>console.log(`PlayAsync Failed ${err}`))
}
sound.current.setOnPlaybackStatusUpdate(
(playBackObjectStatus)=>{
console.log(`Audio Finished Playing: ${playBackObjectStatus.didJustFinish}`)
if(playBackObjectStatus.didJustFinish){
console.log(`Inside the If Condition, Did the Audio Finished Playing?: ${playBackObjectStatus .didJustFinish}`)
props.IncreaseCount();
}
}
)
return (
<View >
<Button title="Play Sound" onPress={PlayAuido} />
</View>
);
}
Problem I am facing
No matter what I do, I can't get the props.IncreaseCount to be called in App.js. Using console.log inside the if condition of setOnPlayBackStatusUpdate, I know that the props.IncreaseCount() method is being called, but the IncreaseCount() function in App.js is never called. Any help is greatly appreciated!
Here is the snack
Inside here please do this
<ChildComponent path={item.path} IncreaseCount={IncreaseCount} index={index} songURL={item.song}/>
Ive changed IncreaseCount={IncreaseCount}
DO lemme know if this helps
You have two ways to call the IncreaseCount function, in the ChildComponent
<ChildComponent IncreaseCount={IncreaseCount} path={item.path} .......
or
<ChildComponent IncreaseCount={() => IncreaseCount()} path={item.path} .......
You made a mistake while passing increaseCount prop to the ChildComponent
Here are to correct ways to do it:
return(
<View style={{marginTop: 10}} >
<ChildComponent path={item.path} IncreaseCount={IncreaseCount} index={index} songURL={item.song}/>
</View>
)
or: IncreaseCount={() => IncreaseCount()}

How to pass array of objects to another screen and display them react native

How do i pass the data of selected checkboxes to the previous screen in react native.
The following is what i have done so far:
This is my SelectProducts screen
import React, {useState, useEffect} from 'react'
import { StyleSheet, Text, View, Alert, Image, ScrollView, TouchableOpacity } from 'react-native';
import Checkbox from 'expo-checkbox';
const SelectProducts = ({route}) => {
const [checkedBox, setCheckedBox] = useState([]);
const [selectedBoxesItem, setSelectedBoxesItem] = useState([]);
const [itemList, setItemList] = useState([]);
const includeSelectedItem = (item, index) => {
const newCheckedBox = [...checkedBox];
newCheckedBox[index] = !newCheckedBox[index];
setCheckedBox(newCheckedBox);
setSelectedBoxesItem({
selectedUniqueKey: item.id,
selectedItemName: item.ad_headline,
selectedItemPrice: item.ad_price,
selectedItemPic: item.ad_picture
});
}
This is the function that I'm using to send the data to the RecordASale screen after clicking on the Done button that is below the list of checkboxes.
const handleSelectedSubmit = () => {
navigation.navigate({
name: 'RecordASale',
params: {
post: [selectedBoxesItem],
},
merge: true,
})
}
And this is the checkbox:
return (
{itemList.map((item, index) => (
<DataTable.Row>
<DataTable.Cell>
<View style={styles.checkboxContainer}>
<Checkbox
key={item.id}
value={checkedBox[index]}
onValueChange={() => includeSelectedItem(item, index)}
color={checkedBox ? '#800080' : undefined}
style={styles.checkbox}
/>
</View>
</DataTable.Cell>
<DataTable.Cell>
<Image source = {{uri: "https://cdn.beraty.com/beraty-ads/"+item.ad_picture}} style = {{ width: 30, height: 30 }} />
</DataTable.Cell>
<DataTable.Cell>{item.ad_headline}</DataTable.Cell>
<DataTable.Cell>{item.ad_price}</DataTable.Cell>
</DataTable.Row>
))}
<View style = {styles.submitButton}>
<Text style = {styles.submitButtonText} onPress={() => handleSelectedSubmit()}>Done</Text>
</View>
</DataTable>
);
}
What i want to achieve is to get the following details for every checkbox selected:
item.id,
item.ad_headline,
item.ad_price,
item.ad_picture
All the above data should be passed from this SelectProducts screen to RecordASale screen
To my own understanding, what I did was that I passed objects to the function of the state below:
const [selectedBoxesItem, setSelectedBoxesItem] = useState([]);
setSelectedBoxesItem({
selectedUniqueKey: item.id,
selectedItemName: item.ad_headline,
selectedItemPrice: item.ad_price,
selectedItemPic: item.ad_picture
});
So when i did this, I only get the last selected checkbox details passed to the RecordASale screen even though i selected more than one checkbox.
This is how i'm getting the details into the RecordASale screen:
const RecordASale = ({route}) => {
return (
{(route.params?.post) ? route.params?.post.map((item, index) => (
<View>
<View key={index}>
<Image source = {{uri: "https://cdn.beraty.com/beraty-ads/"+item.selectedItemPic}} style = {{ width: 30, height: 30 }} />
<Text>{item.selectedItemName}</Text>
<Text>{item.selectedItemPrice}</Text>
</View>
</View>
)) : <Text></Text>}
);
}
I want the same details as above for all selected checboxes to be passed to the other screen, and not just one.
I believe I'm quite close to that but some things are missing in my code. Please help. Thanks.
I only shared the parts I'm having problems with.
You can use useRoute hook
const SelectProducts = ({
route
}) => {
const routes = useRoute();
console.log(routes.params ? .post)
}

I am trying to run this react native screen where it fetches data for me. This gets run when i click the text from a home screen. But it's not working

This is where my home screen is:
import React, { useState } from "react";
import {
StyleSheet,
View,
Text,
Button,
FlatList,
TouchableOpacity,
} from "react-native";
import { globalStyles } from "../styles/global";
import Card from "../shared/card";
import FlatButton from "../shared/button";
import { TextInput } from "react-native-gesture-handler";
import { AntDesign } from "#expo/vector-icons";
import Weather from "./weather";
export default function Home({ navigation }) {
//add state here
const [reviews, setReviews] = useState([
{ title: "Let's Snowboard", rating: 4, body: "blue", key: 1 },
]);
const [city, setCity] = useState("");
return (
<View style={globalStyles.container}>
<View style={styles.searchBox}>
<TextInput
placeholder="search"
placeholderTextColor="lightcoral"
style={styles.searchText}
onChange={(text) => setCity(text)}
/>
<TouchableOpacity style={styles.buttonTouch} onPress={Weather}>
<AntDesign name="search1" size={28} color="lightcoral" />
</TouchableOpacity>
</View>
<FlatList
data={reviews}
renderItem={({ item }) => (
<TouchableOpacity
onPress={() => navigation.navigate("Weather", item)}
>
<Card>
<Text style={globalStyles.titleText}>{item.title}</Text>
</Card>
</TouchableOpacity>
)}
/>
<FlatButton text="Let's snowboard?" />
</View>
);
}
For my weather screen:
import React, { useState } from "react";
import { StyleSheet, Text, View, Image } from "react-native";
import { globalStyles } from "../styles/global";
const Weather = () => {
const [date, setData] = useState([]);
const [icon, setIcon] = useState("");
const [cityDisplay, setCityDisplay] = useState("");
const [desc, setDesc] = useState("");
const [main, setMain] = useState("");
const [humidity, setHumidity] = useState("");
const [pressure, setPressure] = useState("");
const [visibility, setVisibility] = useState("");
const [temp, setTemp] = useState("");
async function fetchWeather() {
try {
const response = await fetch(
"https://samples.openweathermap.org/data/2.5/weather?q=London,uk&appid=***"
);
const json = await response.json();
setData({ data: json });
setTemp({ temp: (json.main.temp - 273.15).toFixed(2) + " C" });
setCityDisplay({ cityDisplay: json.name });
setIcon({ icon: json.weather[0].icon });
setMain({ main: json.weather[0].main });
setHumidity({ humidity: json.main.humidity + " %" });
setPressure({ pressure: json.main.pressure + " hPa" });
setVisibility({
visibility: (json.visibility / 1000).toFixed(2) + " km",
});
} catch (err) {
console.warn("error");
}
}
return (
<View style={styles.weatherBox}>
<View style={styles.weatherHolder}>
<Image
source={{
uri: "http://openweathermap.org/img/wn/" + setIcon + "#2x.png",
}}
style={styles.weatherImage}
/>
<View>
<Text style={styles.temperature}>{temp}</Text>
<Text>{cityDisplay}</Text>
</View>
</View>
</View>
);
};
Essentially, my goal is to click the card text where it says :let's snowboard. Once clicked, it should redirect me to the weather screen where at the moment, it will show me the current temperature and the name of the city. I am not sure why it's not showing. Im assuming it has something to do with my weather screen.
I had tested out making another simple screen where it would show the values of my current state 'reviews'. I was able to click the card and redirect me to another screen where it shows the rating value and the body.
This is the first time I've dealt with apis. Any guidance would be much appreciated(:
I am fairly certain that you do not actually call the fetchWeather function. In the Weather component you define the function with async function fetchWeather() {...}. However, that is only a function definition. You need to actually call it like this: fetchWeather(); which I think you can do right below the definition like the example below.
async function fetchWeather() {
// code that you want to execute in the function
}
// Actual function call
fetchWeather();
EDIT (concerning state management):
I would definitely recommend reading most of the React Hooks documentation to get a full grasp, but I will still address how you handle state.
At this location in the docs it shows exactly what I believe your issue to be.
Whenever you try to set state you do this: setVariable({ variableName: newValue});, but that is how you are supposed to set state inside of a class. However, fetchWeather() is a function, and functions are supposed to update state like this: setVariable(newValue);

React Native: Passing useState() data to unrelated screens

Explanation: I am creating a fitness app, my fitness app has a component called WorkoutTimer that connects to the workout screen, and that screen is accessed via the HomeScreen. Inside the WorkoutTimer, I have an exerciseCount useState() that counts every time the timer does a complete loop (onto the next exercise). I have a different screen called StatsScreen which is accessed via the HomeScreen tab that I plan to display (and save) the number of exercises completed.
What I've done: I have quite literally spent all day researching around this, but it seems a bit harder with unrelated screens. I saw I might have to use useContext() but it seemed super difficult. I am fairly new to react native so I am trying my best haha! I have attached the code for each screen I think is needed, and attached a screenshot of my homeScreen tab so you can get a feel of how my application works.
WorkoutTimer.js
import React, { useState, useEffect, useRef } from "react";
import {
StyleSheet,
Text,
View,
TouchableOpacity,
Button,
Animated,
Image,
SafeAreaView,
} from "react-native";
import { CountdownCircleTimer } from "react-native-countdown-circle-timer";
import { Colors } from "../colors/Colors";
export default function WorkoutTimer() {
const [count, setCount] = useState(1);
const [exerciseCount, setExerciseCount] = useState(0);
const [workoutCount, setWorkoutCount] = useState(0);
const exercise = new Array(21);
exercise[1] = require("../assets/FR1.png");
exercise[2] = require("../assets/FR2.png");
exercise[3] = require("../assets/FR3.png");
exercise[4] = require("../assets/FR4.png");
exercise[5] = require("../assets/FR5.png");
exercise[6] = require("../assets/FR6.png");
exercise[7] = require("../assets/FR7.png");
exercise[8] = require("../assets/FR8.png");
exercise[9] = require("../assets/S1.png");
exercise[10] = require("../assets/S2.png");
exercise[11] = require("../assets/S3.png");
exercise[12] = require("../assets/S4.png");
exercise[13] = require("../assets/S5.png");
exercise[14] = require("../assets/S6.png");
exercise[15] = require("../assets/S7.png");
exercise[16] = require("../assets/S8.png");
exercise[17] = require("../assets/S9.png");
exercise[18] = require("../assets/S10.png");
exercise[19] = require("../assets/S11.png");
exercise[20] = require("../assets/S12.png");
exercise[21] = require("../assets/S13.png");
return (
<View style={styles.container}>
<View style={styles.timerCont}>
<CountdownCircleTimer
isPlaying
duration={45}
size={240}
colors={"#7B4FFF"}
onComplete={() => {
setCount((prevState) => prevState + 1);
setExerciseCount((prevState) => prevState + 1);
if (count == 21) {
return [false, 0];
}
return [(true, 1000)]; // repeat animation for one second
}}
>
{({ remainingTime, animatedColor }) => (
<View>
<Image
source={exercise[count]}
style={{
width: 150,
height: 150,
}}
/>
<View style={styles.timeOutside}>
<Animated.Text
style={{
color: animatedColor,
fontSize: 18,
position: "absolute",
marginTop: 67,
marginLeft: 35,
}}
>
{remainingTime}
</Animated.Text>
<Text style={styles.value}>seconds</Text>
</View>
</View>
)}
</CountdownCircleTimer>
</View>
</View>
);
}
const styles = StyleSheet.create({})
WorkoutScreen.js
import React, { useState } from "react";
import { StyleSheet, Text, View } from "react-native";
import WorkoutTimer from "../components/WorkoutTimer";
export default function WorkoutScreen() {
return (
<View style={styles.container}>
<WorkoutTimer />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center",
},
});
HomeScreen.js
import React from "react";
import { StyleSheet, Text, View, SafeAreaView, Button } from "react-native";
import { TouchableOpacity } from "react-native-gesture-handler";
import { AntDesign } from "#expo/vector-icons";
import { Colors } from "../colors/Colors";
export default function HomeScreen({ navigation }) {
return (
<SafeAreaView style={styles.container}>
<Text style={styles.pageRef}>SUMMARY</Text>
<Text style={styles.heading}>STRETCH & ROLL</Text>
<View style={styles.content}>
<TouchableOpacity
style={styles.timerDefault}
onPress={() => navigation.navigate("WorkoutScreen")}
>
<Button title="START WORKOUT" color={Colors.primary} />
</TouchableOpacity>
<TouchableOpacity
style={styles.statContainer}
onPress={() => navigation.navigate("StatsScreen")}
>
<AntDesign name="barschart" size={18} color={Colors.primary} />
<Text style={{ color: Colors.primary }}>Statistics</Text>
<AntDesign name="book" size={18} color={Colors.primary} />
</TouchableOpacity>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({})
StatsScreen.js
import React from "react";
import { StyleSheet, Text, View } from "react-native";
import { exerciseCount, workoutCount } from "../components/WorkoutTimer";
export default function StatsScreen() {
return (
<View style={styles.container}>
<Text display={exerciseCount} style={styles.exerciseText}>
{exerciseCount}
</Text>
<Text display={workoutCount} style={styles.workoutText}>
{workoutCount}
</Text>
</View>
);
}
const styles = StyleSheet.create({});
Home Screen Image
As far as I can tell, you're almost there! You're trying to get your 2 state
variables from the WorkoutTimer like this:
import { exerciseCount, workoutCount } from "../components/WorkoutTimer";
Unfortunatly this won't work :( . These two variables change throughout your
App's life-time and that kinda makes them "special".
In React, these kinds of variables need to be declared in a parent component
and passed along to all children, which are interested in them.
So in your current Setup you have a parent child relationship like:
HomeScreen -> WorkoutScreen -> WorkoutTimer.
If you move the variables to HomeScreen (HomeScreen.js)
export default function HomeScreen({ navigation }) {
const [exerciseCount, setExerciseCount] = useState(0);
const [workoutCount, setWorkoutCount] = useState(0);
you can then pass them along to WorkoutScreen or StatsScreen with something
like:
navigation.navigate("WorkoutScreen", { exerciseCount })
navigation.navigate("StatsScreen", { exerciseCount })
You'll probably have to read up on react-navigation's documentation for .navigate I'm not sure I remember this correctly.
In order to read the variable you can then:
export default function WorkoutScreen({ navigation }) {
const exerciseCount = navigation.getParam(exerciseCount);
return (
<View style={styles.container}>
<WorkoutTimer exerciseCount={exerciseCount} />
</View>
);
}
and finally show it in the WorkoutTimer:
export default function WorkoutTimer({ exerciseCount }) {
Of course that's just part of the solution, since you'll also have to pass
along a way to update your variables (setExerciseCount and setWorkoutCount).
I encourage you to read through the links I posted and try to get this to work.
After you've accumulated a few of these stateful variables, you might also want to look at Redux, but this is a bit much for now.
Your app looks cool, keep at it!
I ended up solving this problem with useContext if anyone is curious, it was hard to solve initially. But once I got my head around it, it wasn't too difficult to understand.
I created another file called exerciseContext with this code:
import React, { useState, createContext } from "react";
const ExerciseContext = createContext([{}, () => {}]);
const ExerciseProvider = (props) => {
const [state, setState] = useState(0);
//{ exerciseCount: 0, workoutCount: 0 }
return (
<ExerciseContext.Provider value={[state, setState]}>
{props.children}
</ExerciseContext.Provider>
);
};
export { ExerciseContext, ExerciseProvider };
and in App.js I used ExerciseProvider which allowed me to pass the data over the screens.
if (fontsLoaded) {
return (
<ExerciseProvider>
<NavigationContainer>
<MyTabs />
</NavigationContainer>
</ExerciseProvider>
);
} else {
return (
<AppLoading startAsync={getFonts} onFinish={() => setFontsLoaded(true)} />
);
}
}
I could call it with:
import { ExerciseContext } from "../components/ExerciseContext";
and
const [exerciseCount, setExerciseCount] = useContext(ExerciseContext);
This meant I could change the state too! Boom, solved! If anyone needs an explanation, let me know!
I think you have to use Mobx or Redux for state management. That will be more productive for you instead built-in state.

Conditional rendering with React Hooks : loading

I am learning how to use React Hooks and have been stuck for many hours on something that's supposed to be very simple.
I am trying to display a a text if the state variable "loading" is true. If it's false, I want to display something else.
No matter what I try, "loading" is always false or at least, the UI does not appear to reflect its value.
here is the code:
import React, {useState, useEffect} from 'react';
import {View, SafeAreaView, Text} from 'react-native';
const testScreen= (props) => {
const [loading, setLoading ] = useState(true);
useEffect(() => {
setLoading(false);
}, []);
if(loading)
{
return <Text>Hi</Text>;
}
else
{
return<Text.Hey</Text>
}
}
export default testScreen;
Any help will be more than welcome and I am sorry if this is very basic.
UPDATE: Here is the actual code I am working with. SetLoading is supposed to update the state variable to false but never does or at least, the UI des not render.
import React, {useState, useEffect} from 'react';
import {View, SafeAreaView, Text, ActivityIndicator} from 'react-native';
import CategoryTag from '../Components/CategoryTag';
import firestore from '#react-native-firebase/firestore';
const CategoryScreen = (props) => {
const topicCollection = firestore().collection('Topics')
.where("active","==",true);
//hooks for topics
const [topics,setTopics] = useState([]);
const [loading, setLoading ] = useState(true);
//get all active topics
useEffect(() => {
return topicCollection.onSnapshot(querySnapshot => {
const list = [];
querySnapshot.forEach(doc => {
const { active, name } = doc.data();
list.push({
id: doc.id,
active,
name,
});
});
setTopics(list);
setLoading(false);
});
}, []);
const renderTopics = () =>{
return(
topics.map((item) =>{
return(
<CategoryTag key = {item.id}
color={userTopics.includes(item.name) ?"#51c0cc":"#303239"}
name = {item.name}
isSelected = {userTopics.includes(item.name)}
handleClick = {addTopicToUserTopicCollection}
/>
)
})
)
}
if(loading)
{
return (
<SafeAreaView style={{flex:1, backgroundColor:"#455a65"}}>
<View style={{width:200, padding:20, paddingTop:60}}>
<Text style ={{fontSize:25, fontWeight:"bold",
color:"#fff"}}>What are you</Text>
<Text style ={{fontSize:22, color:"#fff"}}>interested in?
</Text>
</View>
<View style={{flex:1, alignItems:"center",
justifyContent:"center", alignSelf:"center"}}>
<ActivityIndicator />
</View>
</SafeAreaView>
)
}
else // this never runs
{
return (
<SafeAreaView style={{flex:1, backgroundColor:"#455a65"}}>
<View>
<View style={{width:200, padding:20, paddingTop:60}}>
<Text style ={{fontSize:25, fontWeight:"bold",
color:"#fff"}}>What are you</Text>
<Text style ={{fontSize:22, color:"#fff"}}>interested in?
</Text>
</View>
<View style ={{flexDirection:"column", paddingTop:20}}>
<View style ={{padding:15, paddingTop:15,
marginBottom:15,
flexWrap:"wrap", flexDirection:"row"}}>
{renderTopics(topics)}
</View>
</View>
</View>
</SafeAreaView>
);
}
}
export default CategoryScreen;
You are immediately setting your setLoading state to false and therefore loading text might be rendering for fraction of second, or not at all, like a glitch. Try setting setLoading with a timeout and then you will see the intended behaviour.
const TestScreen= (props) => {
const [loading, setLoading ] = useState(true);
useEffect(() => {
setTimeout(()=>setLoading(false), 3000);
}, []);
if(loading)
{
return <Text>Hi</Text>;
}
else
{
return<Text>hey</Text>
}
}

Categories