What I am trying to do is alert the company_id that is in local storage.
import React, { Component } from 'react';
import { ActivityIndicator, AsyncStorage, Button, StatusBar, Text, StyleSheet, View, } from 'react-native';
import * as pouchDB_helper from '../utils/pouchdb';
type Props = {};
export default class HomeScreen extends Component<Props> {
render() {
AsyncStorage.getItem('company_id', (err, result) => {
alert(result);
});
return (
<View style={styles.container}>
<Button title="Hi" onPress={this.doSomething} />
</View>
);
}
}
The following code works but I want to be able to do it from inside a helper function. If you see at the top, I have import * as pouchDB_helper from '../utils/pouchdb';
In there I have the following:
import React from 'react';
import { AsyncStorage } from 'react-native';
import PouchDB from 'pouchdb-react-native'
export async function pouchDB_config() {
return AsyncStorage.getItem('company_id', (err, result) => {
return result;
});
}
Instead of AsyncStorage.getItem() code, if I do alert(pouchDB_helper.pouchDB_config()) I get an object with the following: {"_40":0,"_65":0,"_55"_null,"72":null}
I know I'm obviously not doing something right with the whole asynchronous nature of it all so if anyone has any guidance I would greatly appreciate it. I'm still not up to par with how to work with async functions in react native.
This is beacause when you call the function pouchDB_helper.pouchDB_config() it returns a promise.
There are different ways to use this to your advantage.
In your util/pouchdb change the function as following:
export async function pouchDB_config() {
return await AsyncStorage.getItem('company_id');
}
Now you can call this function as follows:
pouchDB_config().then((company_id) => {
console.log(company_id);
});
Or you can call it anywhere else within an async function:
const otherAsyncFunction = async () => {
const company_id = await pouchDB_config();
console.log(company_id);
}
Related
as I continue to work on my issues I've got one that's kind of perplexing. my app has two JS files that interact with each other, but what's odd is it seems that then I call the function to get the data from firestore it seems that it wants to run it twice.
Here is the HomeScreen.js file that is suppoed to call UserInformation
import { Pressable, Text } from 'react-native';
import { useEffect, useId, useState } from 'react';
import { async } from '#firebase/util';
import { FlatList } from 'react-native-gesture-handler';
import {getStorage, ref, getDownloadURL} from 'firebase/storage';
//import the user infomration component
import UserInformation from '../components/UserInformation';
import ClubInformation from '../components/ClubInformation';
import AircraftInformation from '../components/AircraftInformation';
function HomeScreen() {
const userDetails = UserInformation();
const clubDetails = ClubInformation();
const aircraftDetails = AircraftInformation();
if (userDetails !== null) {
//console.log(userDetails)
//console.log(clubDetails)
//console.log(aircraftDetails)
}
if(!userDetails) {
return <Text>Loading...</Text>
}
if(!clubDetails) {
return <Text>Loading...</Text>
}
return <Text>Welcome {userDetails.first} {userDetails.last} {clubDetails.name}! This is a placeholder for future updates in the home screen</Text>
}
export default HomeScreen
the userDetails calls is below:
userInformation.js
import { useState, useEffect } from "react";
import { getAuth } from "firebase/auth";
import { doc, getDoc } from "firebase/firestore";
import { db } from '../components/FirebaseConfig';
export default function UserInformation() {
const [userDetails, setUserDetails] = useState(null);
useEffect(() => {
async function getUserDetails() {
const user = getAuth().currentUser;
const userRef = doc(db, "users", user.uid);
const docSnap = await getDoc(userRef);
if (docSnap.exists()) {
const data = docSnap.data();
setUserDetails(data);
} else {
console.log("No such User!");
}
}
getUserDetails();
}, []);
return userDetails;
}
Whenever I do a console log from the HomeScreen it seems to first return null, then it returns the data. Is there something I'm doing wrong here (I'm sure it is) or is it standard behavior? Thank you in advance
I tried to run this multiple ways but those other functions that i have that also query the db seem to do the same thing. I don't know if it's something up with my async
This is expected behavior when UserInformation function is working as a custom hook.
The first time when HomeScreen component was initialized/mounted, the default value userDetails is null.
After a few seconds, the app fetches data from Firestore and updates userDetails state value which trigger a second rerender.
Note: Recommended best practice is always prefixed custom hooks function name use[FUNCTION_NAME] like useUserInformation.
Your logic is completely wrong. You couldn't wait on referencing hook function.
Even you can use useEffect hook if you want to get valid response.
function HomeScreen() {
const userDetails = UserInformation();
const clubDetails = ClubInformation();
const aircraftDetails = AircraftInformation();
useEffect(()=>{
if (userDetails) {
console.log(userDetails)
}
}, [userDetails])
if(!userDetails) {
return <Text>Loading...</Text>
}
if(!clubDetails) {
return <Text>Loading...</Text>
}
return <Text>Welcome {userDetails.first} {userDetails.last} {clubDetails.name}! This is a placeholder for future updates in the home screen</Text>
}
As I said in my comment you don't need to create your custom hook functions. It's enough with normal functions it will work like this.
async function getUserDetails() {
const user = getAuth().currentUser;
const userRef = doc(db, "users", user.uid);
const docSnap = await getDoc(userRef);
if (docSnap.exists()) {
const data = docSnap.data();
return data;
} else {
console.log("No such User!");
return null
}
}
function HomeScreen() {
const [userDetails, setUserDetails] = useState(null)
useEffect(()=>{
getUserDetails().then(data=>{
if (data != null) {
setUserDetails(data)
}
})
}, [])
if (!userDetails) {
return <Text>Loading...</Text>
}
return <Text>Welcome {userDetails.first} {userDetails.last}</Text>
}
in my react native app I have started playing with custom hooks, I created a hook to retrive user coordinates, however when my useGeolocation hook is called (inside handleUpdateLocation method) I always get the following warning:
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.
Actually, all my components are functional components...
Here is my component:
//import * as React from 'react';
import { View, Text, StyleSheet,TouchableOpacity, Platform,PermissionsAndroid, BackHandler, ScrollView, TextInput, ActivityIndicator ,SafeAreaView} from 'react-native';
import React, { useState, useEffect } from 'react'
import { observer } from 'mobx-react'
import useGeolocation from '#hooks/useGeolocation.js';
export default OrderCard14 = observer((props) =>
{
const handleUpdateLocation = async (gender) =>
{
const { coordinates, city } = useGeolocation(true);
};
return(
<View style={{ flex:1,backgroundColor:'white',alignItems:'center',padding:0 }}>
</View>
);
});
And my simplified hook(removed some functions):
//import AsyncStorage from '#react-native-async-storage/async-storage';
import { AsyncStorage ,Platform, Alert, PermissionsAndroid } from 'react-native';
import _ from 'lodash';
import env from '#env/vars';
import http from '#env/axiosin';
import Geolocation from 'react-native-geolocation-service';
import { useStore } from '#hooks/use-store';
const useGeolocation = (getCityName = false) => {
const root = useStore();
const [coordinates, setCoordinates] = useState(false);
const [city, setCity] = useState(false);
const requestLocationPermissions = async () =>
{
console.log('getting new coordinates');
if (Platform.OS === 'ios')
{
const auth = await Geolocation.requestAuthorization("whenInUse");
if(auth === "granted")
{
//root.mapStore.setLocationEnabled(true);
this.locationEnabled = true;
let coords = await getMe(getCityName);
return coords;
/*
const todayId = moment().isoWeekday();
if(todayId != root.userStore.user.lastLoginDayId)
{
getMe();
}
*/
}
else
{
//Alert.alert('PLEASE ENABLE LOCATION');
root.mapStore.setLocationEnabled(false);
//this.locationEnabled = false;
}
}
if (Platform.OS === 'android')
{
let granted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION);
if (granted === PermissionsAndroid.RESULTS.GRANTED)
{
// do something if granted...
//this.loactionEnabled = true;
root.mapStore.setLocationEnabled(true);
let coords = await getMe();
return coords;
/*
const todayId = moment().isoWeekday();
if(todayId != root.userStore.user.lastLoginDayId)
{
getMe();
}
*/
}
else
{
//Alert.alert('KULO');
root.mapStore.setLocationEnabled(false);
//this.locationEnabled = false;
}
}
}
useEffect(() => {
requestLocationPermissions();
}, []);
console.log('returning new coordinates with hook: '+coordinates);
return { coordinates, city };
};
export default useGeolocation;
What's exactly the problem?
Invalid hook call. Hooks can only be called inside of the body of a function component.
I think they should add to the list of possible/common reasons that:
You might be conditionally invoking the hook call on any given render, or not invoking it at all.
Hooks not only need to be inside the body of a function component, they need to be at the level of the body of the function component. Always called on every render, always in the same order. You have one that's inside a function:
const handleUpdateLocation = async (gender) =>
{
const { coordinates, city } = useGeolocation(true);
};
Which is invalid. Instead, move the hook call to the component level. You can still use the resulting values inside the function:
const { coordinates, city } = useGeolocation(true);
const handleUpdateLocation = async (gender) =>
{
// here you can still use coordinates and city
};
In general, it's a good rule of thumb to invoke all of the hooks that you'll need as the very first thing you do in any given component.
here is the code I am using. it doesn't make any sense to me because the error is basically telling me I am not passing it a function when using query but I am. and I am not calling the function directly either. any help would be a appreciated
import logo from "./logo.svg";
import "./App.css";
import { useEffect, useState } from "react";
//import Posts from './components/Posts'
import axios from "axios";
import { useQuery } from "react-query";
import React from "react";
async function fetchPosts() {
const data = await fetch("http://swapi.dev/api/planetsd");
return data.json();
}
function App() {
const { data, status, error } = useQuery(
"stuffthing",
async () => await fetchPosts()
);
// first argumello\'ent is a string to cache and track the query result
if (status === "error") {
console.log(`${error} new error`);
}
if (status === "loading") {
return <div>loading</div>;
}
return (
<div className="container">
<h1>Posts</h1>
{data}
</div>
);
}
export default App;
I just ran into the exact same issue and it took me wayyy too long to figure out what was going on.
If you're following the documentation of v3, useQuery would indeed be used as such:
const { data, status, error } = useQuery("posts", async () => await fetchPosts());
However, in v4 it has changed to take an array as a first parameter:
const { data, status, error } = useQuery(["posts"], async () => await fetchPosts());
Three hours of my life I'll never get back.
I have a straightforward react component that looks so in AllWords.js :
import React, { useEffect, useState } from 'react';
import consts from '../../constants/Constants.js';
function AllWords() {
const [words, setWords] = useState([]);
async function fetchData(){
const response= await fetch(consts.FETCH_URL);
const data = await (response.json());
setWords(data);
};
// API: useEffect( () => { . . . return cleanup; },[var_n_whose_change_triggers_useEffect . . .] );
useEffect(() => {fetchData()}, [] );
return (
<>
{
words.map(w=> <div>{w.word}</div>)
}
</>
);
}
export default AllWords;
I would like to refactor the fetchData() method out of the component into another file (basically a separate .js file that holds the fetch call).
What I would like is to have created a file titled FetchAllWords.js under src/actions/ & then import it. & use that.
I have several questions :
do I need to set the state in the FetchAllWords.js and then useSelector to extract the state in AllWords.js?
in FetchAllWords.js do I need to usedispatch to dispatch a method call setting the state? I would like to just setState in FetchAllWords.js and then extract it in AllWords.js. This is what I have so far:
import consts from '../constants/Constants.js';
import { useState } from 'react';
async function FetchAllWords(){
const [words, setWords] = useState([]);
const response= await fetch(consts.FETCH_URL);
const data = await (response.json());
setWords(data);
}
export default FetchAllWords;
I am unsure how to import this and use it in AllWords.js. I am using the following statement :
import wordList from '../../actions/FetchAllWords';
Then I am trying to use wordList as a handle to the file '../../actions/FetchAllWords.js' & attempting to access the async function FetchAllWords so wordList.FetchAllWords();
Firstly , the editor (VSCode) won't let me see the function despite the import call.
Secondly I am getting an error (something like) :
TypeError: _actions_FetchAllWords_js__WEBPACK_IMPORTED_MODULE_3__.default.FetchAllWords is not a function
Any insight or help would be appreciated since rather uneasy with JS & React.
The github repo is : https://github.com/mrarthurwhite/hooks-p5-react-redux
EDIT: As per David's suggestions :
So AllWords.js React component is :
import React, { useEffect, useState } from 'react';
import wordList from '../../services/Fetch.js';
function AllWords() {
const [words, setWords] = useState([]);
function fetchData(){
wordList.fetchAllWords().then(
data => setWords(data)
);
};
// API: useEffect( () => { . . . return cleanup; },[var_n_whose_change_triggers_useEffect . . .] );
useEffect(() => {fetchData()}, [] );
return (
<>
{
words.map(w=> <div>{w.word}</div>)
}
</>
);
}
export default AllWords;
And Fetch.js is :
import consts from '../constants/Constants.js';
class Fetch {
async fetchAllWords(){
const response= await fetch(consts.FETCH_URL);
const data = await (response.json());
return data;
}
}
export default Fetch;
No, don't worry about state in the external file. Just focus on the one thing it should do, perform the AJAX operation. At its simplest it's just a function, something like:
import consts from '../../constants/Constants.js';
const fetchAllWords = async () => {
const response = await fetch(consts.FETCH_URL);
const data = await (response.json());
return data;
}
export default fetchAllWords;
You can even make it a class which contains this function, if you plan on adding other service operations as well. (Fetch specific word? Find word? etc.) The point is that this does just one thing, provide data. Let the React components handle React state.
Within the component you'd just use that to get your data. Something like:
import React, { useEffect, useState } from 'react';
import fetchAllWords from '../../services/FetchAllWords.js';
function AllWords() {
const [words, setWords] = useState([]);
useEffect(() => {
fetchAllWords().then(w => setWords(w));
}, []);
return (
<>
{
words.map(w=> <div>{w.word}</div>)
}
</>
);
}
export default AllWords;
Overall it's a matter of separating concerns. The service performs the AJAX operation and returns the meaningful data, internally concerned with things like JSON deserialization and whatnot. The React component maintains the state and renders the output, internally concerned with updating state after useEffect runs and whatnot.
When this component loads, the startSync function starts. However, I also have an exit function that navigates to a login screen if pressed during any of this.
What happens though is, if I press the exit button, it brings me back to the login page. However, the async function is still running so when it finishes it will then navigate me to the Identification screen.
I was wondering if there is a way to cancel all Async function's so that nothing is running in the background after the exit button is pushed.
import React, { Component } from 'react';
import { ActivityIndicator, AsyncStorage, Button, StatusBar, Text, StyleSheet, View, } from 'react-native';
import * as pouchDB_helper from '../utils/pouchdb';
type Props = {};
export default class SyncScreen extends Component<Props> {
startSync = async () => {
pouchDB_helper.sync().then((response) => {
AsyncStorage.setItem('initial_sync', 'true');
//navigate to the identification page
this.props.navigation.navigate('Identification');
}, (error) => { Alert.alert("Error", "Syncing failed. Please try again."); });
}
exit = async () => {
await AsyncStorage.clear();
this.props.navigation.navigate('Login');
}
componentDidMount() {
this.startSync();
}
static navigationOptions = {
title: 'Syncing Settings',
};
render() {
return (
<View style={styles.container}>
<Text style={styles.footerText} onPress={this.exit}>Exit</Text>
</View>
);
}
}