Handling async funtion of Firebase - javascript

So i'm using firebase as my database for my mobile app that i create with react native. I once use it in a web app, and i remember that there is some part of firebase that is async function. Now when i want to use firebase in react, i meet the same problem, and i forgot how to solve it.
function viewName(userId) {
firebase.database().ref('player/' + userId).on('value', (snapshot) => {
const userName = snapshot.val.name;
return userName;
});
}
export default function App() {
return (
<View style={styles.container}>
<Text>Open up App.js to start working on your app!</Text>
<Text>{viewName('0')}</Text>
</View>
);
}
It's only a mock app, not the real app. I'm trying to receive the name of user with key "0" (player/0/name), then show it as on the app screen. But as u can guess,the viewName() function return nothing...
Can someone help me?
(Sorry if it's sounds stupid or if my question make no sense. First time asking here, and first time using react)

function viewName(userId, cb) {
firebase.database().ref('player/' + userId).on('value', (snapshot) => {
const userName = snapshot.val.name;
cb(userName);
});
}
export default function App() {
const [username, setUsername] = React.useState('')
React.useEffect(() => {
viewName('0', setUsername)
}, [])
return (
<View style={styles.container}>
<Text>Open up App.js to start working on your app!</Text>
<Text>{username}</Text>
</View>
);
}

Related

React Native Async Storage setItem call fails

I'm writing a simple Async Storage set Item call in React Native to store my store's cart data. But the call fails. I can't find possible reason why it so as there is no mention of such in the documentation. I have added the relevant code. Please help would be appreciated.
const ProductDetailScreen = (props) => {
const {product} = props.route.params;
const getCart = async () => {
return await AsyncStorage.getItem('cart');
};
const cart = getCart();
const addToCart = async () => {
try {
await AsyncStorage.setItem('cart', [
...cart, JSON.stringify(product),
]);
} catch (e) {
console.error(`Failed to add item: {${JSON.stringify(product)}} to cart`);
}
};
return (
<>
<TouchableOpacity onPress={addToCart}>
<Button>
<Text style={styles.btnText}>Add to Cart</Text>
</Button>
</TouchableOpacity>
</>
)
};
export default ProductDetailScreen;
error log
ERROR Failed to add item: {{"id":2,"name":"Scene Stealers","description":"Corset cuts","price":250,"image":5,"colors":["#0F140D","#DD8560","#E1E0DB"],"sizes":["S","M","L"]}} to cart
You can only store/set string data in AsyncStorage while you are trying to store Array
await AsyncStorage.setItem('cart', JSON.stringify([...cart, JSON.stringify(product)]))

Firebase only fetches data on second or third attempt

I'm building an app with React and Firestore.
In one feature, I need to use some specific user data to query data from another collection, and show that on the app.
Specifically I want to use users.books, which returns an array, to query the collection books.
However, for some reason the users.books doesn't load on first render. It typically takes 2-3 renders to fetch the books.user data. This is despite the currentUserUID being loaded right away.
I've tried using a loading state as specified in How to wait for Firebase data to be fetched before progressing?, but to no avail.
Do I need to use the onSnapShot method?
Thanks for reading
My code
import 'firebase/firestore'
import { booksRef} from '../../App';
const ProfileScreen = ({ navigation }) => {
const currentUserUID = firebase.auth().currentUser.uid;
const [firstName, setFirstName] = useState('');
const [userBookTitles, setUserBookTitles] = useState([]);
const [userBooks, setUserBooks] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function getUserInfo(){
let doc = await firebase
.firestore()
.collection('users')
.doc(currentUserUID)
.get();
if (!doc.exists){
Alert.alert('No user data found!')
} else {
let dataObj = doc.data();
setFirstName(dataObj.firstName)
setUserBookTitles(dataObj.books)
console.log(userBookTitles)
}
}
getUserInfo();
}, [])
useEffect(() => {
async function getUserBooks() {
booksRef.where("title", "in", userBookTitles).onSnapshot(snapshot => (
setUserBooks(snapshot.docs.map((doc) => ({id: doc.id, ...doc.data()})))
))
}
setLoading(false);
getUserBooks()
}, [])
if (!loading) {
return (
<View style={styles.container}>
<Text> Hi {firstName} </Text>
<TouchableOpacity onPress={handlePress}>
<Text> Log out </Text>
</TouchableOpacity>
<Row
books={userBooks}
/>
</View>
);
} else {
return (
<View style={styles.container}>
<Text> Test </Text>
</View>
);
}
};
So it's worth noting that your setX methods may, or may not, complete in the sequence you have them in your code. Therefore, your booksRef call could be being made even though userBookTitles is an empty array. Which would explain why you're not getting any data on load.
You're setting userBookTitles in your first useEffect and the only other place I see you're using it is in your booksRef call. One easy fix would be to simple move booksRef inside the else statement of the first useEffect and simply pass it the userBookTitles there. This should help in solving your issue, if I understood it correctly.

React/React Native: Changes in a child component's state re-render entire components

Here is the simple representation of the code of the component:
/*
All the necessary imports
*/
export default function ParentComponent() {
const [parentState, setParentState] = useState(null);
const {username, setUsername} = useContext(Username);
const route = useRoute();
const navigation = useNavigation();
useEffect(()=>{
//Logic to call setParentstate based on query results;
})
const FirstComponent = () => {
const [firstState, setFirstState] = useState(null);
useEffect(()=>{
//Logic to call setFirststate based on query results;
},[])
return (
<View>
<Text>{firstState}</Text>
</View>
)
};
const SecondComponent = () => {
const [secondState, setSecondState] = useState(0);
const {number, setNumber} = useContext(Number);
useEffect(()=>{
//Logic to call setSecondstate and setNumber based on query results;
},[])
const updateState = () => {
setSecondState(secondState + 1);
setNumber(secondState + 1);
};
return (
<View>
<Text>{secondState}</Text>
<TouchableOpacity onPress={updateState}>
<Text>Update</Text>
</TouchableOpacity>
</View>
);
};
return (
<View>
<Text>{username}</Text>
<Text>{parentState}</Text>
<FirstComponent />
<SecondComponent />
</View>
);
}
There is a functionality to update state and reflect the change on the SecondComponent. Since I am not changing the ParentComponent's state, this should not trigger re-rendering of the ParentComponent. However, all of the components get re-rendered when the secondState is updated.
I am confused because updates in the state of the child component should not trigger re-rendering of the parent component, but this keeps happening.
I suspect this has to do with hooks in the parent component or use of Context API, but am not sure.
Can anyone help me out in identifying what might be causing this, and how I should fix it?
Thanks a lot!
I saw a similar issue, it was solved by defining just one component per file.

How to access results from API to display to my UI? (React Native)

I'm currently making an API call inside my App component in React Native. I'm using fetch to capture a few values and storing them in variables within my fetch. How can I access those varaiables inside my return statement to display to my UI?
This is what my component looks like right now, I need "lessonDesc" and "lessonName" values inside my return statement where you see "NEED DATA HERE"
export default function App() {
fetch(contentURL).then((response) => response.json()).then((json) => {
let lessonName = json.fields.lessonName
console.log(json.fields.lessonName);
let lessonDesc = json.fields.lessonDesc
console.log(json.fields.lessonDesc);
return lessonDesc
}).catch((error) => {
console.error(error);
})
return (
<View style={styles.container}>
<Text>{NEED DATA HERE}</Text>
<StatusBar style="auto" />
</View>
);
}```
Best way that is you use useState for updating variables and you can access that variables in all around in the your Component , also you are be able to define states separately or all together , in this case we defined separately. So you can try it:
export default function App() {
const [lessonName, setLessonName] = React.useState("");
const [lessonDesc, setLessonDesc] = React.useState("");
React.useEffect(()=>{
fetch(contentURL).then((response) =>
response.json()).then((json) => {
setLessonName(
json.fields.lessonName
);
setLessonDesc(
json.fields.lessonDesc
);
}).catch((error) => {
console.error(error);
})
} , []);
return (
<View style={styles.container}>
<Text>{NEED DATA HERE}</Text>
<StatusBar style="auto" />
</View>
);
}

App function returning nothing from asynchronous operation in React Native

I'm totally new to React Native, and this is the first code I've written. I'm unsure how to return my jsx from my App() enclosing function when it has an asynchronous operation within it. I suspect this is a really basic error, so hopefully my code will show the problem clearly:
import React from 'react';
import { Text, View, TextInput, Button, Alert } from 'react-native';
import AsyncStorage from '#react-native-community/async-storage';
//check if logged in, if not show login screen
export default function App() {
AsyncStorage.getItem("user")
.then((user) => {
if(user) {
//user validated
return (
<View>
<Text>Logged In!</Text>
</View>
);
} else {
//user not logged in, therefore show login screen
return (
<View>
<Text>Enter Your Username</Text>
<TextInput
maxLength={16}
/>
<Button
title="Login"
/>
</View>
);
}
});
}
The outer App() function is obviously not returning anything, but I'm unsure of how to make it wait and return the jsx from the inner asynchronous function.
What I've tried:
I initially used async/await on the outer function and AsyncStorage.getItem(...), but that returned the following error:
Error: Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead.
Next I tried a different approach after reading an answer to a similar question here at SO:
export default function App() {
return (
<View>
<Login />
</View>
);
async function Login() {
const user = await AsyncStorage.getItem("user");
if(user) {
return (
<Text>Logged In!</Text>
);
} else {
return (
<Text>Enter Your Username</Text>
);
}
}
}
But am getting the same error as above when I used async/await.
I'm a rank newbie to React Native, so please hit me with the "for dummies" explanation.
As I said in the comments since your JSX is wrapped in a promise(because async functions always return a promise), React says Objects are not valid as a React child.
You can use useState and useEffect hooks to maintain the user state and to get the user. For example:
function Login() {
const [user, setUser] = useState();
useEffect(() => {
if(!user) {
AsyncStorage.getItem('user')
.then(user => setUser(user));
// .catch()
}
}, [user, setUser]) // also add any other required dependencies
if(user) {
return (
<Text>Logged In!</Text>
);
} else {
return (
<Text>Enter Your Username</Text>
);
}
}
I prefer class components and as such, I'd do this:
export default class App extends Component {
constructor(props) {
super(props)
this.state = {
user: null //intialize a user state with null
}
}
componentDidMount = () => {
const user = await AsyncStorage.getItem("user");//get the user on component mount and set state to user
this.setState({ user }) // this.setState({ user: user }) does same
}
render() {
const { user } = this.state; //destructure state here
return (
<>
<View>
<Login />
</View>
{
user ? //if user show logged in else show enter username
<Text>Logged In!</Text> :
<Text>Enter Your Username</Text>
}
</>
)
}
}

Categories