Firebase v9 not adding docs - javascript

I'm working with Firebase v9. The authentication works fine, but the Firestore does not work me for some reason. I don't even get an error--it just doesn't do anything.
I tried addDocs() but still nothing works.
EDIT: actually , i was using the firebase #9.1.0 i upgraded it to #9.6.7 and it worked perfectly fine ! i had to downgrade from #9.6.8 ( the latest ) to #9.1.0 because of the error ( Can't find variable: IDBIndex ) !
import React, { useLayoutEffect, useState } from "react";
import {
Text,
View,
StyleSheet,
TextInput,
TouchableOpacity,
KeyboardAvoidingView,
Platform,
ScrollView,
Alert,
} from "react-native";
import { AntDesign, Ionicons } from "#expo/vector-icons";
import { doc, setDoc } from "firebase/firestore";
import { db } from "../../firebase/firebaseConfig";
const NewChat = ({ navigation }) => {
const [input, setInput] = useState("");
useLayoutEffect(() => {
navigation.setOptions({
title: "Add a new Chat",
headerBackTitle: "Chats",
});
}, [navigation]);
const AddChat = async () => {
const myDoc = doc(db, "Chats", input);
const docData = {
chatName: input,
};
setDoc(myDoc, docData).then(() => {
navigation.goBack();
});
};
return (
<ScrollView>
<View
style={{
marginTop: 20,
marginHorizontal: 20,
borderColor: "black",
borderWidth: 1,
}}
>
<View style={styles.container}>
<AntDesign
name="wechat"
size={40}
color="black"
style={{ alignSelf: "center" }}
/>
<TextInput
placeholder="Enter a chat name"
value={input}
onChangeText={(text) => {
setInput(text);
}}
style={{ flexGrow: 1, marginLeft: 20 }}
/>
<TouchableOpacity style={{ alignSelf: "center" }} onPress={AddChat}>
<Ionicons name="checkmark-done-circle" size={40} color="black" />
</TouchableOpacity>
</View>
</View>
</ScrollView>
);
};
const styles = StyleSheet.create({
container: {
flexDirection: "row",
backgroundColor: "white",
justifyContent: "center",
height: 60,
},
});
export default NewChat;

The function setDoc() asynchronously returns a promise which means all you're missing is the await keyword before you call the function.
const AddChat = async () => {
const myDoc = doc(db, "Chats", input);
const docData = {
chatName: input,
};
await setDoc(myDoc, docData).then(() => {
navigation.goBack();
});
};
Edit: I think I see the real problem, It has to do with the v9 document reference. Try using collection() within the document reference.
const AddChat = async () => {
const myDoc = doc(collection(db, "Chats"), input);
const docData = {
chatName: input,
};
await setDoc(myDoc, docData).then(() => {
navigation.goBack();
});
};

Related

Cannot read data from firestore database to react native project

I am relatively new to react natuve and I am reading data from my firestore db and I can see the title and id in my console after:
console.log(doc.id, doc.data());
but the items dont show in my flatlist and it just displays a constant loading symbol
this is my code and i cannot figure out why it wont load into my flatlist. Any help is appreciated
`
import { Pressable, Text, TextInput, View, StyleSheet,
SafeAreaView, FlatList, ActivityIndicator } from 'react-native'
import React, { useState, useEffect } from 'react'
import Exercise from '../../components/Exercise'
import { MaterialIcons } from '#expo/vector-icons';
import { auth, app, db, getFirestore, collection, addDoc, getDocs } from '../../firebase'
const WorkoutScreen = () => {
const [exerciseTitle, setTitle] = useState('');
const [exerciseList, setExerciseList] = useState([]);
const addExercise = async() => {
try {
const docRef = await addDoc(collection(db, "Exercise"), {
title: exerciseTitle,
isChecked: false,
});
console.log("Document written with ID: ", docRef.id);
setTitle("");
} catch (e) {
console.error("Error adding document: ", e);
}
};
const getExercise = async() => {
const querySnapshot = await getDocs(collection(db, "Exercise"));
querySnapshot.forEach((doc) => {
console.log(doc.id, doc.data());
setExerciseList({
...doc.data(),
id: doc.id,
title: doc.exerciseTitle,
});
});
};
useEffect ( () => {
getExercise();
}, []);
return (
<SafeAreaView style={styles.container}>
<View style={styles.header}>
{/* heading */}
<Text style={styles.heading}>Workout List</Text>
{/* shopping items */}
<Text style={styles.noOfExercises}>0</Text>
{/* delete all */}
<Pressable>
<MaterialIcons name="delete" size={32} color="black" />
</Pressable>
</View>
{exerciseList.length > 0 ? (
<FlatList
data={exerciseList}
renderItem ={({item}) => <ExerciseItem title={item.exerciseTitle}/>}
keyExtractor={(item) => item.id}
/>
) : (
<ActivityIndicator />
)}
<TextInput
placeholder='Enter Excercise'
style={styles.input}
value={exerciseTitle}
onChangeText={(text) => setTitle(text)}
onSubmitEditing={addExercise}
/>
</SafeAreaView>
)
}
export default WorkoutScreen
const styles = StyleSheet.create({
container: {
flex: 1,
},
header: {
flexDirection: 'row',
width: '90%',
alignSelf: 'center',
padding: 10,
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 10,
},
heading: {
fontWeight: '900',
fontStyle: 'bold',
fontSize: 30,
flex: 1,
marginTop: 20,
},
noOfExercises: {
fontSize: 20,
fontWeight: '500',
marginRight: 20,
},
input: {
backgroundColor: 'lightgrey',
padding: 10,
fontSize: 17,
width: '90%',
alignSelf: 'center',
marginTop: 'auto',
borderRadius: 10,
}
})
`
The problem is here:
setExerciseList({
...doc.data(),
id: doc.id,
title: doc.exerciseTitle,
});
You're setting exerciseList to be an object, yet your rendering code expects it to be an array:
{exerciseList.length > 0 ? ...
Since you don't define a length property in the object, it ends up always render the <ActivityIndicator />.
What you'll want to do is set the entire array of documents in the state, for example with:
const getExercise = async() => {
const querySnapshot = await getDocs(collection(db, "Exercise"));
setExerciseList(querySnapshot.docs.map((doc) => {
return { ...doc.data(), id: doc.id };
});
};

SOLVED Expo-Camera, takePictureAsync undefined (Unhandled promise rejection: TypeError: ref.current.takePictureAsync is not a function)

I'm trying to create react app with expo using expo-camera for taking pictures. I have separately components MeasurementCameraScreen and MeasurementCamera. I'm using useRef() hook to be able to call takePictureAsync() from the MeasuremenCameraScreen.
When pressing the take image -button takePicture() console.logs the ref, so I assume the onPress gets there, but then I get the following error message:
[Unhandled promise rejection: TypeError: ref.current.takePictureAsync is not a function. (In 'ref.current.takePictureAsync(options)', 'ref.current.takePictureAsync' is undefined)]
I saw that people have also had same issues with takePictureAcync(), but I haven't found solution to my problem. I also tired to combine the MeasurementCameraScreen and MeasurementCamera components to one component, and with that I got the camera working, but I'm curious of why it doesn't work now? refs are new thing for me so I think there is something wrong with them.
Here are the components:
MeasurementCameraScreen
import { useRef } from 'react'
import { StyleSheet, TouchableOpacity, View } from 'react-native'
import MeasurementCamera from '../components/MeasurementCamera'
import Text from '../components/Text'
const MeasurementCameraScreen = () => {
const cameraRef = useRef(null)
return (
<View style={styles.container}>
<View style={styles.cameraContainer}>
<MeasurementCamera ref={cameraRef}/>
</View>
<View>
</View>
<TouchableOpacity
onPress={() => cameraRef.current.takePicture()}
style={styles.buttonContainer}
>
<Text>
Take image
</Text>
</TouchableOpacity>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
cameraContainer: {
flex: 1,
},
buttonContainer: {
width: '100%',
height: 70,
backgroundColor: 'white',
justifyContent: 'center',
alignItems: 'center',
alignSelf: 'flex-end'
},
})
export default MeasurementCameraScreen
MeasurementCamera
import { useState, useEffect, useImperativeHandle, forwardRef } from 'react'
import { StyleSheet } from "react-native"
import { Camera } from 'expo-camera'
import Text from './Text'
const MeasurementCamera = forwardRef((props, ref) => {
const [hasPermission, setHasPermission] = useState(null)
useEffect(() => {
const getPermission = async () => {
const { status } = await Camera.requestCameraPermissionsAsync()
setHasPermission(status === 'granted')
}
getPermission()
}, [])
const takePicture = async () => {
if (ref) {
console.log(ref.current)
const options = {
quality: 1,
base64: true
}
const picture = await ref.current.takePictureAsync(options)
console.log(picture.uri)
}
}
useImperativeHandle(ref, () => ({
takePicture
}))
if (hasPermission === null) {
return <Text>Requesting for camera permission</Text>
} if (hasPermission === false) {
return <Text>No access to camera</Text>
}
return (
<Camera
ref={ref}
style={StyleSheet.absoluteFillObject}
/>
)
})
MeasurementCamera.displayName = 'MeasurementCamera'
export default MeasurementCamera
EDIT: Solved the problem, check out the comment for solution! :)
Okay I found the solution!
After reading the medium article #LouaySleman recommended about the expo-camera I understood that to be able to use the Expo Camera components functions I need to use ref. So what I needed was two refs, one for the components to communicate and another one to be able to use the Camera takePictureAsync() function.
Now that we have permissions to access the Camera, you should get familiar with the ref props on line 132, in the Camera component. There we have passed the cameraRef that was previously defined with useRef. In doing so, we will have access to interesting methods that we can call to control the Camera.
What you needed is to add two refs, one for the components to communicate and another one to be able to use the Camera takePictureAsync() function Please check this example from medium:
import React, { useState, useRef, useEffect } from "react";
import {
View,
Text,
TouchableOpacity,
SafeAreaView,
StyleSheet,
Dimensions,
} from "react-native";
import { Camera } from "expo-camera";
import { Video } from "expo-av";
export default function CameraScreen() {
const [hasPermission, setHasPermission] = useState(null);
const [cameraType, setCameraType] = useState(Camera.Constants.Type.back);
const [isPreview, setIsPreview] = useState(false);
const [isCameraReady, setIsCameraReady] = useState(false);
const [isVideoRecording, setIsVideoRecording] = useState(false);
const [videoSource, setVideoSource] = useState(null);
const cameraRef = useRef();
useEffect(() => {
(async () => {
const { status } = await Camera.requestPermissionsAsync();
setHasPermission(status === "granted");
})();
}, []);
const onCameraReady = () => {
setIsCameraReady(true);
};
const takePicture = async () => {
if (cameraRef.current) {
const options = { quality: 0.5, base64: true, skipProcessing: true };
const data = await cameraRef.current.takePictureAsync(options);
const source = data.uri;
if (source) {
await cameraRef.current.pausePreview();
setIsPreview(true);
console.log("picture", source);
}
}
};
const recordVideo = async () => {
if (cameraRef.current) {
try {
const videoRecordPromise = cameraRef.current.recordAsync();
if (videoRecordPromise) {
setIsVideoRecording(true);
const data = await videoRecordPromise;
const source = data.uri;
if (source) {
setIsPreview(true);
console.log("video source", source);
setVideoSource(source);
}
}
} catch (error) {
console.warn(error);
}
}
};
const stopVideoRecording = () => {
if (cameraRef.current) {
setIsPreview(false);
setIsVideoRecording(false);
cameraRef.current.stopRecording();
}
};
const switchCamera = () => {
if (isPreview) {
return;
}
setCameraType((prevCameraType) =>
prevCameraType === Camera.Constants.Type.back
? Camera.Constants.Type.front
: Camera.Constants.Type.back
);
};
const cancelPreview = async () => {
await cameraRef.current.resumePreview();
setIsPreview(false);
setVideoSource(null);
};
const renderCancelPreviewButton = () => (
<TouchableOpacity onPress={cancelPreview} style={styles.closeButton}>
<View style={[styles.closeCross, { transform: [{ rotate: "45deg" }] }]} />
<View
style={[styles.closeCross, { transform: [{ rotate: "-45deg" }] }]}
/>
</TouchableOpacity>
);
const renderVideoPlayer = () => (
<Video
source={{ uri: videoSource }}
shouldPlay={true}
style={styles.media}
/>
);
const renderVideoRecordIndicator = () => (
<View style={styles.recordIndicatorContainer}>
<View style={styles.recordDot} />
<Text style={styles.recordTitle}>{"Recording..."}</Text>
</View>
);
const renderCaptureControl = () => (
<View style={styles.control}>
<TouchableOpacity disabled={!isCameraReady} onPress={switchCamera}>
<Text style={styles.text}>{"Flip"}</Text>
</TouchableOpacity>
<TouchableOpacity
activeOpacity={0.7}
disabled={!isCameraReady}
onLongPress={recordVideo}
onPressOut={stopVideoRecording}
onPress={takePicture}
style={styles.capture}
/>
</View>
);
if (hasPermission === null) {
return <View />;
}
if (hasPermission === false) {
return <Text style={styles.text}>No access to camera</Text>;
}
return (
<SafeAreaView style={styles.container}>
<Camera
ref={cameraRef}
style={styles.container}
type={cameraType}
flashMode={Camera.Constants.FlashMode.on}
onCameraReady={onCameraReady}
onMountError={(error) => {
console.log("camera error", error);
}}
/>
<View style={styles.container}>
{isVideoRecording && renderVideoRecordIndicator()}
{videoSource && renderVideoPlayer()}
{isPreview && renderCancelPreviewButton()}
{!videoSource && !isPreview && renderCaptureControl()}
</View>
</SafeAreaView>
);
}
const WINDOW_HEIGHT = Dimensions.get("window").height;
const closeButtonSize = Math.floor(WINDOW_HEIGHT * 0.032);
const captureSize = Math.floor(WINDOW_HEIGHT * 0.09);
const styles = StyleSheet.create({
container: {
...StyleSheet.absoluteFillObject,
},
closeButton: {
position: "absolute",
top: 35,
left: 15,
height: closeButtonSize,
width: closeButtonSize,
borderRadius: Math.floor(closeButtonSize / 2),
justifyContent: "center",
alignItems: "center",
backgroundColor: "#c4c5c4",
opacity: 0.7,
zIndex: 2,
},
media: {
...StyleSheet.absoluteFillObject,
},
closeCross: {
width: "68%",
height: 1,
backgroundColor: "black",
},
control: {
position: "absolute",
flexDirection: "row",
bottom: 38,
width: "100%",
alignItems: "center",
justifyContent: "center",
},
capture: {
backgroundColor: "#f5f6f5",
borderRadius: 5,
height: captureSize,
width: captureSize,
borderRadius: Math.floor(captureSize / 2),
marginHorizontal: 31,
},
recordIndicatorContainer: {
flexDirection: "row",
position: "absolute",
top: 25,
alignSelf: "center",
justifyContent: "center",
alignItems: "center",
backgroundColor: "transparent",
opacity: 0.7,
},
recordTitle: {
fontSize: 14,
color: "#ffffff",
textAlign: "center",
},
recordDot: {
borderRadius: 3,
height: 6,
width: 6,
backgroundColor: "#ff0000",
marginHorizontal: 5,
},
text: {
color: "#fff",
},
});

How to access JSON API from a link in another API (Json Data have link to another Json)

I want to Ftech the Data on The "link" on a New Screen [Edit] This what I did and What I didn't
call api to:
https://flow.simpas.ai/hortus/paysagiste/category?businessid=0899607494
Done
I receive a json that will allow me to display The Data on My
DataTable Done
When we click on a line of the Datatable Done
I should call api via the link 'link' ['link' is in the first json
and is a link to a second json] Not Yet
I should Display the Data on Link on a New Screen Edit Not Yet
if Someone Can Help in this 2 last Step I'll Be thankful 🙏🏻🙏🏻🙏🏻🙏🏻🙏🏻🙏🏻
This is The Code Of The List Screen:
import React, { useState,useEffect } from 'react';
import { StyleSheet, SafeAreaView, ScrollView,Text, View, Modal,FlatList } from 'react-native';
import { DataTable } from 'react-native-paper';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import COLORS from '../src/conts/colors';
import Button from '../src/views/components/Button';
const List = ({navigation}) => {
const [data,setData] = useState([])
const [loading,setLoading]= useState(true)
const fetchData = ()=>{
fetch("https://flow.simpas.ai/hortus/paysagiste/category?businessid=0899607494")
.then(res=>res.json())
.then((json)=>setData(json))
.catch((error)=>console.error(error))
.finally(()=>setLoading(false))
}
useEffect(()=>{
fetchData()
},[])
const RenderCollection = (item) =>{
return(
<DataTable.Row style={styles.tableRow} key={item.businessid}>
<DataTable.Cell textStyle={{color: '#777777',fontFamily: 'Roboto'}} onPress={() => navigation.navigate("Edit",{item})} >{item.businessid}</DataTable.Cell>
<DataTable.Cell textStyle={{color: '#FEB296', fontFamily: 'Roboto'}}>{item.title}</DataTable.Cell>
<DataTable.Cell textStyle={{color: '#777777', fontFamily: 'Roboto'}}>{item.price}</DataTable.Cell>
</DataTable.Row>
)
}
return (
<SafeAreaView style={{ flex: 1}}>
<ScrollView
contentContainerStyle={{paddingTop: 50, paddingHorizontal: 20}}>
<Text style={{color: COLORS.black, fontSize: 40, fontWeight: 'bold', fontFamily: 'Roboto',textAlign: 'center'}}>
List of Companies
</Text>
<Text style={{color: COLORS.grey, fontSize: 18, marginVertical: 10, fontFamily: 'Roboto', textAlign: 'center'}}>
Check Our Companies Details
</Text>
<DataTable style={styles.container} >
<DataTable.Header style={styles.tableHeader} >
<DataTable.Title textStyle={{color: '#fff',fontSize: 16, fontFamily: 'Roboto'}}>id</DataTable.Title>
<DataTable.Title textStyle={{color: '#fff',fontSize: 16, fontFamily: 'Roboto'}}>Title</DataTable.Title>
<DataTable.Title textStyle={{color: '#fff',fontSize: 16, fontFamily: 'Roboto'}}>body</DataTable.Title>
</DataTable.Header>
</DataTable>
<FlatList
data={data.articles}
renderItem={({item})=>{
return RenderCollection(item)
}}
keyExtractor={item=>item.businessid}
onRefresh={()=>fetchData()}
refreshing={loading}
/>
</ScrollView>
</SafeAreaView>
);
};
export default List;
const styles = StyleSheet.create({
container: {
padding: 0,
},
tableHeader: {
backgroundColor: '#50E3C2',
},
tableRow: {
backgroundColor: '#fff',
},
});
Im not sure if you got your problem resolved so I created a demo to point you in the right direction. To simulate your api and use your data table, I used a cat fact api with cat breeds as business names, string lengths as prices, and random strings as business ids. To simulate an api giving a link to another api I created this:
// api to simulate your api
export default async function (url,{query={},onSuccess,onFail,onComplete}){
let res, json ={};
try{
url= new URL(url)
res = await fetch(url)
res = await res?.join?.() || res;
res = await res.json();
json = {
articles:res.articles || generateArticles(res.data),
nextLink:res.next_page_url,
prevLink:res.prev_page_url,
page:res.current_page
}
//hipolab api was having network resource issues
const apiIsWorking = false
// if hipo api doesnt work just load the github json
json.otherApi = apiIsWorking
?'http://universities.hipolabs.com/search'
:'https://raw.githubusercontent.com/Hipo/university-domains-list/master/world_universities_and_domains.json';
// here would be where you setData
onSuccess?.(json);
}catch(err){
onFail(err)
}finally{
// here would be where you setLoading
onComplete?.(json)
}
}
// to simulate your api i will try to convert cat facts
// into something your data table will render
const generateArticles = data=>{
return data.map(item=>{
let keys = Object.keys(item)
return {
businessid:getRandomChars(3)+'-'+Math.floor(getRandom(0,999)).toString().padStart(3,'0'),
title:item.breed || item[keys[0]],
price:item.pattern.length || item[keys[1]].length,
country:item.country
}
})
}
export const getRandomChars=(len=1)=>{
const characters = 'abcdefghijklmnopqrstuvwxyz'
let str = ''
while (str.length < len){
let index = Math.floor(getRandom(0,characters.length))
str+=characters[index].toUpperCase()
}
return str
}
const getRandom = (min=0,max=1)=>{
let range = max - min
return Math.random() * range + min
}
Which is imported here:
import React,{
useEffect,
useState
} from 'react';
import {
Text,
View,
StyleSheet,
SafeAreaView,
ScrollView,
FlatList,
} from 'react-native';
import Constants from 'expo-constants';
import { Card, DataTable } from 'react-native-paper';
import api from '../api/first';
import COLORS from '../colors';
import RenderCollection from '../components/ListItem';
import Button from '../components/Button'
import ListHeader from '../components/ListHeader'
export default function List({navigation,route}) {
const [data,setData] = useState([])
const [loading,setLoading]= useState(true);
const fetchData = (url='https://catfact.ninja/breeds')=>{
api(url,{
onSuccess:res=>{
// console.log('Success!\n',JSON.stringify(res,null,2))
setData(res)
},
onFail:err=>{
console.log('Failure!\n',err)
},
onComplete:res=>{
// console.log('Success!\n',JSON.stringify(res,null,2))
setLoading(false)
}
})
}
const next=()=>{
if(data.nextLink){
fetchData(data.nextLink)
}
}
const prev=()=>{
if(data.prevLink){
fetchData(data.prevLink)
}
}
const RenderItem=({item,index})=>{
return (
<RenderCollection otherApi={data.otherApi} item={item} navigation={navigation} index={index}/>
)
}
useEffect(()=>{
fetchData()
},[])
// listen for route param changes and update
// data when it does change
useEffect(()=>{
const {item, index} = route.params || {}
if(!item ){
console.log('no item to update')
return
}
if(isNaN(index)){
console.log('no index passed to update')
return
}
const articles = [...data.articles];
articles[index] = item
setData({...data,articles})
},[route.params?.item])
return (
<SafeAreaView style={{ flex: 1}}>
<Text style={{color: COLORS.black, fontSize: 40, fontWeight: 'bold', fontFamily: 'Roboto',textAlign: 'center'}}>
List of Companies
</Text>
<Text style={{color: COLORS.grey, fontSize: 18, marginVertical: 10, fontFamily: 'Roboto', textAlign: 'center'}}>
Check Our Companies Details
</Text>
<View style={styles.buttonRow}>
<View style={styles.rowItem}>
<Button title="Prev" onPress={prev} disabled={!data.prevLink}/>
</View>
<View style={styles.rowItem}>
<Text>Page {data.page}</Text>
</View>
<View style={styles.rowItem}>
<Button title="Next" onPress={next} disabled={!data.nextLink}/>
</View>
</View>
<View style={styles.dataTableWrapper}>
<DataTable style={{flex:1}}>
<FlatList
style={{flex:1}}
data={data.articles}
renderItem={RenderItem}
keyExtractor={item=>item.businessid}
onRefresh={()=>fetchData()}
refreshing={loading}
ListHeaderComponent={ListHeader}
stickyHeaderIndices={[0]}
/>
</DataTable>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
dataTableWrapper:{
height:'40%',
width:'100%',
overflow:'hidden'
},
buttonRow:{
marginVertical:5,
flexDirection:'row',
width:'100%',
justifyContent:'space-between'
},
rowItem:{
justifyContent:'center',
alignItems:'center',
},
});
To summarize, the first api returns a list of cat breeds, links to the prev/next page of list, and a link to an api for getting universities of a country. Each cat breed has a country property. So I joined the two:
import React from 'react';
import {
StyleSheet
} from 'react-native';
import {DataTable, useTheme} from 'react-native-paper';
import tableStyle from './tableStlye';
import {lightenColor} from '../colors'
export default function({item,index,navigation,otherApi}){
const colors = useTheme().colors
const mainColor = colors.primary;
const lightColor1 = lightenColor(mainColor,0.35)
const lightColor2 = lightenColor(mainColor,0.5)
let themedStyle ={
borderBottomWidth:1,
backgroundColor:index%2== 0 ?lightColor2 : lightColor1
}
return(
<DataTable.Row style={[styles.tableRow,themedStyle]} key={item.businessid}>
<DataTable.Cell
textStyle={{color: '#777777',fontFamily: 'Roboto'}}
style={tableStyle.id}
onPress={() => navigation.navigate("Edit",{item,key:'businessid',index})}
>
{item.businessid}
</DataTable.Cell>
{/* HERE THE API IS SENT TO THE NEXT SCREEN */}
<DataTable.Cell
textStyle={{color: '#FEB296', fontFamily: 'Roboto'}}
style={tableStyle.title}
onPress={()=>navigation.navigate("Universities",{item,otherApi})}
>
{item.title}
</DataTable.Cell>
<DataTable.Cell
style={tableStyle.price}
textStyle={{color: '#777777', fontFamily: 'Roboto'}}
>
{item.price}
</DataTable.Cell>
</DataTable.Row>
)
}
const styles = StyleSheet.create({
tableHeader: {
backgroundColor: '#50E3C2',
},
tableRow: {
backgroundColor: '#fff',
},
})
And finally the universities screen:
import React,{
useState,
useEffect,
useRef
} from 'react';
import {
View,
StyleSheet,
Text,
FlatList
} from 'react-native';
import {useTheme, Headline} from 'react-native-paper';
import {getRandomChars} from '../api/first';
import api from '../api/second';
import UniversityItem from '../components/UniversityItem';
import TextInput from '../components/TextInput';
import Button from '../components/Button';
import {lightenColor} from '../colors';
const fakeRouteParams = {
item:{
country:"United States",
},
otherApi:'https://raw.githubusercontent.com/Hipo/university-domains-list/master/world_universities_and_domains.json'
}
export default function Universities({navigaton,route}){
const {item,otherApi} = route?.params || fakeRouteParams;
const [universities, setUniversites] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [uniFilter, setUniFilter] = useState('');
const filteredUniversities = uniFilter
? universities.filter(uni=>uni.name.toLowerCase().includes(uniFilter.toLowerCase()))
: universities
const colors = useTheme().colors;
const mainColor = colors.primary;
const lightColor = lightenColor(mainColor,0.5)
const fetchData = async()=>{
setIsLoading(true);
await api(otherApi,{
query:{
country:item.country,
limit:50
},
onSuccess:res=>{
setUniversites(res)
},
onFail:err=>{
console.log(err)
alert(err)
setIsLoading(false)
},
onComplete:()=>{
setIsLoading(false)
}
})
}
useEffect(()=>{
fetchData()
},[])
useEffect(()=>{
},[uniFilter])
return (
<View style={styles.container}>
<View style={[styles.title,{borderColor:colors.primary}]}>
<Text style={styles.titleText}>{item.country+"'s"} Universities</Text>
</View>
<View style={styles.flatListContainer}>
<TextInput
label="Search By Name"
style={{width:'95%'}}
onChangeText={setUniFilter}
value={uniFilter}
dense
/>
<FlatList
style={{flex:1}}
data={filteredUniversities}
renderItem={props=><UniversityItem {...props} colors={[mainColor,lightColor]}/>}
keyExtractor={(item,index)=>index+'-'+getRandomChars(10)}
refreshing={isLoading}
onRefresh={fetchData}
/>
</View>
</View>
)
}
const styles=StyleSheet.create({
container:{
flex:1,
// justifyContent:'center',
// alignItems:'center'
},
flatListContainer:{
width:'95%',
height:'60%',
alignItems:'center'
},
title:{
width:'100%',
borderBottomWidth:2
},
titleText:{
fontWeight:'bold',
fontSize:20,
textAlign:'center'
},
header:{
flexDirection:'row',
width:'95%',
alignItems:'center',
justifyContent:'space-between',
padding:5,
}
})

TypeError: undefined is not an object (evaluating '_this.camera = _ref')

-App dev in React Native-
Hello,
I have a problem with Expo Camera. Here an error is referred when you want to take a picture.
"TypeError: undefined is not an object (evaluating '_this.camera = _ref')" / Scan.js.
If the app is freshly updated with Expo, everything works. But as soon as you continue programming and another error occurs, this error appears and doesn't go away until you refresh the app again.
I have tried a lot, but I need help here.
Scan.js
import React, { Component, useState, useEffect } from 'react';
import { View, Text, StyleSheet, TouchableOpacity, Image } from 'react-native';
import {launchCamera, launchImageLibrary} from 'react-native-image-picker';
import {Camera, Constants} from 'expo-camera';
import * as MediaLibrary from 'expo-media-library';
import * as Haptics from 'expo-haptics';
import Images from '../assets/icon/index'
const Scan = () => {
const [hasPermission, setHasPermission] = useState(null);
const [type, setType] = useState(Camera.Constants.Type.back);
const [status, requestPermission] = MediaLibrary.usePermissions();
useEffect(() => {
(async () => {
const { status } = await Camera.requestCameraPermissionsAsync();
setHasPermission(status === 'granted');
})();
}, []);
if (hasPermission === null) {
return <View/>;
}
if (hasPermission === false) {
return <Text>No access to camera</Text>;
}
takePicture = async () => {
if (this.camera) {
let photo = await this.camera.takePictureAsync();
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);
console.log(photo.uri);
MediaLibrary.saveToLibraryAsync(photo.uri);
}
};
return (
<View style={styles.container}>
<Camera style={styles.camera}
type={type}
ref={ref => {
this.camera = ref;
}}>
<View style={styles.buttonContainer}>
<TouchableOpacity
style={styles.button}
onPress={() => {
setType(
type === Camera.Constants.Type.back
? Camera.Constants.Type.front
: Camera.Constants.Type.back
);
}}
>
<Image source={Images.camera} style={styles.icon}></Image>
</TouchableOpacity>
<TouchableOpacity
style={styles.button}
onPress={takePicture}
>
<Text style={styles.text}>Take</Text>
</TouchableOpacity>
</View>
</Camera>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
camera: {
flex: 1,
},
buttonContainer: {
flex: 1,
backgroundColor: 'transparent',
flexDirection: 'row',
margin: 20,
top: 0,
},
button: {
flex: 0.1,
alignSelf: 'flex-end',
alignItems: 'center',
},
text: {
fontSize: 18,
color: 'white',
},
icon : {
tintColor: 'white',
},
})
export default Scan; ```
Create a new camera reference and attach it to the Camera component.
import { useRef } from 'react';
...
const cameraRef = useRef<Camera>(null);
...
<Camera ref={cameraRef} ... />
In your takePicture function replace this.camera.takePictureAsync with cameraRef.current?.takePictureAsync
Error: Function components cannot have string refs. We recommend using useRef() instead. Learn more about using refs safely here: https://reactjs.org/link/strict-mode-string-ref.
This is because the guy who answered used TypeScript.
Simply replace
const cameraRef = useRef(null);
with
const cameraRef = useRef(null);

delete list element using filter method

I am working on todo list in native , i have a function called onDelete but when i click on it , it delete all element in list and after then program crashed.
this is my main file where i have stored value as key , value pair
export default function App() {
const [courseGoal, setCourseGoal] = useState([]);
const [count, setCount] = useState('');
const submitHandler = () => {
setCourseGoal((currGoal) => [
...currGoal,
{ key: Math.random().toString(), value: count },
]);
};
console.log('App', courseGoal)
return (
<View style={styles.container}>
<SearchBar
setCourseGoal={setCourseGoal}
count={count}
setCount={setCount}
submitHandler={submitHandler}
/>
<ListItem courseGoal={courseGoal} setCourseGoal={setCourseGoal} courseGoal={courseGoal}/>
</View>
);
}
this is my list component where i am facing issue, you can see ondelete here.
import React from "react";
import { StyleSheet, Text, TouchableOpacity } from "react-native";
import { FlatList } from "react-native-web";
export default function ListItem(props) {
const onDelete = (goalId) => {
props.setCourseGoal((currGoal) => {
currGoal.filter((goal) => goal.key !== goalId);
console.log("clicked", props.courseGoal[0].key);
});
};
return (
<FlatList
data={props.courseGoal}
keyExtractor={(item, index) => item.key}
renderItem={(itemData) => (
<TouchableOpacity
onPress={onDelete.bind(itemData.item.key)}
activeOpacity={0.2}
>
<Text style={styles.listData}>{itemData.item.value}</Text>
{console.log(itemData.item.key)}
</TouchableOpacity>
)}
/>
);
}
this is my main component where i have my search input
import React from "react";
import { View, Text, StyleSheet, Pressable, TextInput } from "react-native";
export default function SearchBar(props) {
const onInputHandler = (value) => {
props.setCount(value);
};
return (
<View style={styles.searchBox}>
<Pressable style={styles.submitBtn} title="Click Me !">
<Text>☀️</Text>
</Pressable>
<TextInput
style={styles.searchInput}
placeholder="Enter Goal"
onChangeText={onInputHandler}
/>
<Pressable
style={styles.submitBtn}
title="Click Me !"
onPress={props.submitHandler}
>
<Text>🔥🔥</Text>
</Pressable>
</View>
);
}
const styles = StyleSheet.create({
searchBox: {
flexDirection: "row",
justifyContent: "space-around",
},
searchInput: {
width: "90%",
textAlign: "center",
padding: 10,
// color: 'white',
borderRadius: 10,
borderWidth: 1,
marginHorizontal: 5,
},
submitBtn: {
color: "black",
justifyContent: "space-around",
padding: 10,
borderRadius: 10,
borderWidth: 1,
},
});
You have to return the filtered array from your onDelete function
const onDelete = (goalId) => {
props.setCourseGoal((currGoal) => {
return currGoal.filter((goal) => goal.key !== goalId);
});
};

Categories

Resources