Related
I'm trying to use a useRef hook so a scrollview and my pan gesture handler can share a common ref. but once I initialize the useRef() hook and pass it to both components, it breaks with this error
TypeError: Attempted to assign to readonly property.
I've tried typecasting and adding types to the useRef call but it returns the same error. Can someone help please?
My component:
import { StyleSheet, Text, View, Image, Dimensions } from "react-native";
import React from "react";
import {
PanGestureHandler,
PanGestureHandlerGestureEvent,
PanGestureHandlerProps,
} from "react-native-gesture-handler";
import Animated, {
runOnJS,
useAnimatedGestureHandler,
useAnimatedStyle,
useSharedValue,
withTiming,
} from "react-native-reanimated";
import { FontAwesome } from "#expo/vector-icons";
export interface InfluencerItemProps
extends Pick<PanGestureHandlerProps, "simultaneousHandlers"> {
id?: string;
name: string;
userName: string;
profileImg: string;
rating?: Number;
onDismiss?: (Item: InfluencerItemProps) => void;
}
const ITEM_HEIGHT = 65;
const { width: SCREEN_WIDTH } = Dimensions.get("window");
const TRANSLATE_X_THRESHOLD = -SCREEN_WIDTH * 0.3;
const InfluencerItem = (props: InfluencerItemProps) => {
const { name, userName, profileImg, onDismiss, simultaneousHandlers } = props;
const translateX = useSharedValue(0);
const marginVertical = useSharedValue("2%");
const R_Height = useSharedValue(ITEM_HEIGHT);
const opacity = useSharedValue(1);
const panGesture = useAnimatedGestureHandler<PanGestureHandlerGestureEvent>({
onActive: (event) => {
translateX.value = event.translationX;
},
onEnd: () => {
const shouldbeDismissed = translateX.value < TRANSLATE_X_THRESHOLD;
if (shouldbeDismissed) {
translateX.value = withTiming(-SCREEN_WIDTH);
R_Height.value = withTiming(0);
marginVertical.value = withTiming("0%");
opacity.value = withTiming(0, undefined, (isFinished) => {
if (isFinished && onDismiss) {
runOnJS(onDismiss)(props);
}
});
} else {
translateX.value = withTiming(0);
}
},
});
const rStyle = useAnimatedStyle(() => ({
transform: [
{
translateX: translateX.value,
},
],
}));
const rIconContainerStyle = useAnimatedStyle(() => {
const opacity = withTiming(
translateX.value < TRANSLATE_X_THRESHOLD ? 1 : 0
);
return { opacity };
});
const RContainerStyle = useAnimatedStyle(() => {
return {
height: R_Height.value,
opacity: opacity.value,
marginVertical: marginVertical.value,
};
});
return (
<Animated.View style={[styles.wrapper, RContainerStyle]}>
<Animated.View style={[styles.iconContainer, rIconContainerStyle]}>
<FontAwesome name="trash" size={ITEM_HEIGHT * 0.5} color="white" />
</Animated.View>
<PanGestureHandler
simultaneousHandlers={simultaneousHandlers}
onGestureEvent={panGesture}
>
<Animated.View style={[styles.container, rStyle]}>
<Image
source={{
uri: profileImg,
}}
style={styles.image}
/>
<View style={styles.text}>
<Text style={styles.name}>{name}</Text>
<Text style={styles.userName}>{userName}</Text>
</View>
</Animated.View>
</PanGestureHandler>
</Animated.View>
);
};
export default InfluencerItem;
const styles = StyleSheet.create({
wrapper: {
width: "100%",
alignItems: "center",
},
container: {
flexDirection: "row",
borderWidth: 1,
borderRadius: 12,
height: ITEM_HEIGHT,
width: "100%",
backgroundColor: "#FFFFFF",
},
image: {
marginVertical: 10,
marginHorizontal: "4%",
height: 48,
width: 48,
borderRadius: 50,
},
text: {
justifyContent: "center",
alignItems: "flex-start",
marginHorizontal: 6,
},
name: {
fontSize: 14,
fontWeight: "500",
color: "#121212",
},
userName: {
fontSize: 12,
fontWeight: "400",
color: "#121212",
},
iconContainer: {
height: ITEM_HEIGHT,
width: ITEM_HEIGHT,
backgroundColor: "red",
position: "absolute",
right: "2.5%",
justifyContent: "center",
alignItems: "center",
},
});
InfluencerItem.defaultProps = {
name: "UserName",
userName: "userName",
profileImg:
"https://d2qp0siotla746.cloudfront.net/img/use-cases/profile-picture/template_0.jpg",
rating: "4",
};
This is my Screen:
import {
StyleSheet,
Text,
View,
SafeAreaView,
TextInput,
ScrollView,
} from "react-native";
import { StatusBar } from "expo-status-bar";
import React, { useCallback, useRef, useState } from "react";
import InfluencerItem from "../../components/InfluencerItem";
import { InfluencerItemProps } from "../../components/InfluencerItem";
// import { ScrollView } from "react-native-gesture-handler";
type Props = {};
const Saved = (props: Props) => {
const [search, setSearch] = useState<string>("");
const [influencerData, setInfluencerData] = useState(influencerz);
const handleSearch = () => {
console.log(search);
};
const onDismiss = useCallback((Item: InfluencerItemProps) => {
setInfluencerData((influencers) =>
influencers.filter((item) => item.id !== Item.id)
);
}, []);
const ref = useRef(null); //useRef initialization
return (
<SafeAreaView style={{ flex: 1 }}>
<View style={styles.container}>
<View style={styles.saved}>
{/* Saved Kikos component goes in here */}
<ScrollView ref={ref}> //passed ref here
{influencerData.map((influencer) => (
<InfluencerItem
key={influencer.id}
name={influencer.name}
userName={influencer.handle}
profileImg={influencer.image}
onDismiss={onDismiss}
simultaneousHandlers={ref} //also passed ref here
/>
))}
</ScrollView>
</View>
<Text style={styles.bottomText}>No Saved Kikos again</Text>
</View>
</SafeAreaView>
);
};
export default Saved;
const styles = StyleSheet.create({
container: {
paddingHorizontal: "4%",
},
headerText: {
color: "#121212",
fontWeight: "700",
lineHeight: 30,
fontSize: 20,
marginTop: 40,
},
search: {
borderRadius: 12,
backgroundColor: "#D9D9D9",
fontSize: 14,
lineHeight: 21,
color: "#7A7B7C",
paddingLeft: 10,
paddingRight: 5,
height: 45,
marginTop: 15,
position: "relative",
},
innerSearch: {
position: "absolute",
top: 30,
right: 10,
},
saved: {
backgroundColor: "rgba(217, 217, 217, 0.15)",
marginTop: 22,
paddingVertical: "7%",
marginBottom: 34,
},
bottomText: {
fontSize: 14,
fontWeight: "500",
textAlign: "center",
},
});
Use createRef() instead of useRef() as mentioned in the documentation.
const imagePinch = React.createRef();
return (
<RotationGestureHandler
simultaneousHandlers={imagePinch}
....
There is a complete example here in TypeScript.
Also make sure to use Animated version of components wherever applicable (<Animated.View> instead of <View>, <Animated.Image> instead of <Image> etc)
I have the below code to dynamically render a component with data from an API. However I keep getting this error:
Error: Objects are not valid as a React child (found: object with keys {_U, _V, _W, _X}). If you meant to render a collection of children, use an array instead.
In the code I tried to create an array with the data but still continue to get the error. There is just one object that I extract from the API data to render.
import React, { useState } from 'react';
import { Button, View, Text, Image, TextInput, StyleSheet, ScrollView, SafeAreaView, TouchableOpacity } from 'react-native';
import { Video } from 'expo-av';
import { AsyncStorage } from 'react-native';
import { Ionicons } from '#expo/vector-icons';
export default async function Profile({navigation}) {
const [company, setCompany] = useState([])
const getData = async () => {
try {
const value = await AsyncStorage.getItem('company')
console.log(value)
fetchData(value)
if(value !== null) {
}
} catch(e) {
}
}
const fetchData = (name) => {
fetch("https://stonernautz.com/companies.json")
.then(response => {
return response.json()
})
.then(data => {
for(var i in data){
if(data[i].name == name)
console.log(data[i])
setCompany("["+JSON.stringify(data[i])+"]")
console.log(company)
}
})
}
useEffect(() => {
getData()
});
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center', backgroundColor:"black",color:"white"}}>
<View style={styles.catView}>
<Ionicons name="arrow-back" color={'white'} size={26} style={{marginLeft:0, marginTop:10}} onPress={() => navigation.goBack()}/>
<Text style={{color:"white"}}>Back</Text>
</View>
<View style={{height:"60%",position:'absolute', top:110}}>
<ScrollView>
{company.map(user => (
<View style={{marginBottom:100}} key={user.name}>
<Video
source={{ uri: `${user.video}` }}
rate={1.0}
volume={1.0}
isMuted={false}
resizeMode="contain"
useNativeControls
style={{ width: 400, height: 250, maxWidth:"100%" }}
/>
<Image
style ={styles.image}
source={{ uri : `${user.logo}`}}
/>
<Text style={styles.name}>{user.name}</Text>
<TouchableOpacity
style={styles.button}
>
<Text>Connect</Text>
</TouchableOpacity>
</View>
))}
</ScrollView>
</View>
</View>
);
}
const styles = StyleSheet.create({
input: {
height: 40,
margin: 12,
borderWidth: 5,
borderBottomColor: '#000000',
borderTopColor: '#000000',
borderRightColor: '#000000',
borderLeftColor: '#000000',
padding: 10,
borderColor: "#000000"
},
image: {
width: 50,
height: 50,
borderRadius: 100,
overflow: "hidden",
marginTop: 20,
marginLeft:20
},
button: {
alignItems: "center",
backgroundColor: "white",
padding: 10,
width:200,
position:'absolute',
top:300,
left:'25%',
height:40,
justifyContent: 'center'
},
container: {
flex: 1,
paddingTop:10,
},
scrollView: {
position:'absolute',
top:80,
left:0,
marginHorizontal: 20,
maxHeight:400,
width:"90%",
maxWidth:"100%",
},
text: {
fontSize: 100,
marginLeft:10,
},
catText: {
color: "white",
fontSize:24,
marginLeft:40,
lineHeight:50
},
catView: {
paddingRight: 20,
position:"absolute",
top:50,
left:20
},
name: {
color:'white',
fontSize:20,
position:'absolute',
top:260,
left:110,
justifyContent: 'center',
alignItems: 'center'
},
});
getData is async so you should try to await the Promise with something like this:
useEffect(() => {
(async () => {
await getData()
})();
}, []);
I figured it out after A While, Here is what I did:
import React, { useState, Component } from 'react';
import { Button, View, Text, Image, TextInput, StyleSheet, ScrollView, SafeAreaView, TouchableOpacity } from 'react-native';
import { Video } from 'expo-av';
import { AsyncStorage } from 'react-native';
import { Ionicons } from '#expo/vector-icons';
const styles = StyleSheet.create({
input: {
height: 40,
margin: 12,
borderWidth: 5,
borderBottomColor: '#000000',
borderTopColor: '#000000',
borderRightColor: '#000000',
borderLeftColor: '#000000',
padding: 10,
borderColor: "#000000"
},
image: {
width: 50,
height: 50,
borderRadius: 100,
overflow: "hidden",
marginTop: 20,
marginLeft:20
},
button: {
alignItems: "center",
backgroundColor: "white",
padding: 10,
width:200,
position:'absolute',
top:300,
left:'25%',
height:40,
justifyContent: 'center'
},
container: {
flex: 1,
paddingTop:10,
},
scrollView: {
position:'absolute',
top:80,
left:0,
marginHorizontal: 20,
maxHeight:400,
width:"90%",
maxWidth:"100%",
},
text: {
fontSize: 100,
marginLeft:10,
},
catText: {
color: "white",
fontSize:24,
marginLeft:40,
lineHeight:50
},
catView: {
paddingRight: 20,
position:"absolute",
top:50,
left:20
},
name: {
color:'white',
fontSize:20,
position:'absolute',
top:260,
left:110,
justifyContent: 'center',
alignItems: 'center'
},
});
class ProductList extends Component {
constructor(props) {
super(props)
this.state = {
company: []
}
}
fetchData = (company) => {
//console.log("hi"+ company)
fetch("https://stonernautz.com/companies.json")
.then(response => {
return response.json()
})
.then(data => {
for(var i in data){
if(data[i].name == company){
this.setState({ company: data[i] })
//console.log(this.state)
}
}
})
}
componentDidMount = async () => {
const value = await AsyncStorage.getItem('company')
//console.log(value)
await this.fetchData(value)
}
render() {
return (
<View style={{backgroundColor:"black", height:"100%", alignItems:"center", justifyContent:"center"}}>
<Text style={{color:"white", alignItems:"center", justifyContent:"center"}}>{this.state.company.name}</Text>
</View>
)
}
}
export default ProductList;
I am trying to develop a mobile app with React Native using Firestore as a database. I'm trying to update a specific document when the "handleLike" function is called. I've passed the id of the posts through handleLike function. I'm trying to update the "comment" field for the sake of the trial. The problem is it updates all of the documents in the database. Also handleLike function is called without me pressing the like button whenever I refresh the page. I get that it is in a loop but I couldn't figure out what I'm doing wrong. I would very much appreciate your help. I have added screenshots of the database and the console.
import { React, useState, useEffect } from "react";
import {
View,
Text,
StyleSheet,
Image,
FlatList,
TouchableOpacity,
} from "react-native";
import { Ionicons, MaterialIcons } from "#expo/vector-icons";
import { useNavigation } from "#react-navigation/core";
import { auth, storage, firestore,firebase } from "../firebase";
import { preventAutoHideAsync } from "expo-splash-screen";
import moment from 'moment';
function Feed() {
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(true);
const [userData, setUserData] = useState(null);
//const [deleted, setDeleted] = useState(false);
const fetchPosts = async () => {
try {
const list = [];
const profileRef = firestore.collection("users").doc(auth.currentUser.uid);
const doc = await profileRef.get();
await firestore
.collection("posts")
.orderBy('postTime', 'desc')
.get()
.then((querySnapshot) => {
console.log("Total Posts: ", querySnapshot.size);
querySnapshot.forEach((doc) => {
const pId = doc.id
const {
userId,
userName,
userSurname,
post,
postImg,
postTime,
userImg,
likes,
comments,
likeByUsers } = doc.data();
list.push({
id: pId,
userId,
userName,
userSurname,
userImg:userImg,
post,
postImg,
postTime: postTime,
liked: false,
likes,
comments,
likeByUsers,
});
});
});
setPosts(list);
if (loading) {
setLoading(false);
}
console.log("Posts: ", posts);
} catch (e) {
console.log(e);
}
};
useEffect(() => {
fetchPosts();
}, []);
const renderPost = (posts) => {
const handleLike = (id) => {
console.log(id)
firestore.collection('posts')
.doc(id)
.update({
comment: "tryout 2"
})
}
return (
<View style={styles.feedItem}>
<Image source={{uri: posts.userImg}} style={styles.avatar} />
<View style={{ flex: 1 }}>
<View
style={{
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
}}
>
<View>
<Text style={styles.name}>{posts.userName} {posts.userSurname}</Text>
<Text style={styles.timestamp}>{moment(posts.postTime.toDate()).fromNow()}</Text>
</View>
<MaterialIcons name="expand-more" size={24} color="#73788B" />
</View>
<Text style={styles.post}>{posts.id}</Text>
<Image
source={{uri: posts.postImg}}
style={styles.postImage}
resizeMode="cover"
/>
<View style={{ flexDirection: "row" }}>
<TouchableOpacity onPress={handleLike(posts.id)}>
<Ionicons
name="heart-outline"
size={24}
color="#73788B"
style={{ marginRight: 16 }}
/>
</TouchableOpacity>
<Ionicons name="chatbox-outline" size={24} color="#73788B" />
</View>
</View>
</View>
);
};
return (
<View style={styles.container}>
<View style={styles.header}>
<Text style={styles.headerTitle}>Feed</Text>
<TouchableOpacity style={styles.addButton}>
<Ionicons name="add-circle" size={32} color="black" />
</TouchableOpacity>
</View>
<FlatList
style={styles.feed}
data={posts}
renderItem={({ item }) => renderPost(item)}
keyExtractor={(item) => item.id}
showsVerticalScrollIndicator={false}
></FlatList>
</View>
);
}
const styles = StyleSheet.create({
addButton: {
position: "absolute",
right: 30,
top: 60,
},
container: {
flex: 1,
backgroundColor: "#EBECF4",
},
header: {
paddingTop: 64,
paddingBottom: 16,
backgroundColor: "#FFF",
alignItems: "center",
justifyContent: "center",
borderBottomWidth: 1,
borderBottomColor: "#EBECF4",
shadowColor: "#454D65",
shadowOffset: { height: 5 },
shadowRadius: 15,
shadowOpacity: 0.2,
zIndex: 10,
},
headerTitle: {
fontSize: 20,
fontWeight: "500",
},
feed: {
marginHorizontal: 16,
},
feedItem: {
backgroundColor: "#FFF",
borderRadius: 5,
padding: 8,
flexDirection: "row",
marginVertical: 8,
},
avatar: {
width: 36,
height: 36,
borderRadius: 18,
marginRight: 16,
},
name: {
fontSize: 15,
fontWeight: "500",
color: "#454D65",
},
surname: {
fontSize: 15,
fontWeight: "500",
color: "#454D65",
},
timestamp: {
fontSize: 12,
color: "#808588",
marginTop: 4,
},
post: {
marginTop: 16,
fontSize: 14,
color: "#838899",
},
postImage: {
width: 267,
height: 150,
borderRadius: 5,
marginVertical: 16,
},
});
export default Feed;
Console Screenshot:
Firestore Database Screenshot:
i am facing a problem regarding async storage in react native .
when i setItem in async storage and then retrieve it if there are 3 tasks- which i have added,
only two of them is retrieved
i will share a photo of before and after
this is output before refreshing
this is the output after refreshing the app
This is my app.js code
import { StatusBar } from 'expo-status-bar';
import {
StyleSheet,
Text,
View,
KeyboardAvoidingView,
FlatList,
TextInput,
TouchableOpacity,
Keyboard,
} from 'react-native';
import React, { Component } from 'react';
import * as Font from 'expo-font';
import Task from './components/Task';
import AppLoading from 'expo-app-loading';
import AsyncStorage from '#react-native-async-storage/async-storage';
let customFonts = {
Poppins_SemiBold: require('./assets/Poppins-SemiBold.ttf'),
Poppins_Regular: require('./assets/Poppins-Regular.ttf'),
};
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
task: '',
taskItems: [],
fontsLoaded: false,
};
}
async _loadFontsAsync() {
await Font.loadAsync(customFonts);
this.setState({ fontsLoaded: true });
}
componentDidMount() {
this._loadFontsAsync();
this._retrieveData()
}
_retrieveData = async () => {
try {
const value = await AsyncStorage.getItem('data');
if (value.length !== 2) {
// We have data!!
this.setState({taskItems:[...JSON.parse(value)]})
console.log(value);
}
} catch (error) {
// Error retrieving data
console.log(error)
}
};
handleAddTask=()=>{
Keyboard.dismiss()
this.setState({taskItems:[...this.state.taskItems,this.state.task]})
this.setState({task:''})
AsyncStorage.setItem('data',JSON.stringify(this.state.taskItems))
}
deleteItem=(index)=>{
try {
let arr = [...this.state.taskItems];
arr.splice(index, 1);
this.setState({taskItems:arr})
AsyncStorage.setItem('data',JSON.stringify(arr))
} catch (err) {
console.log(err);
}
}
render() {
if (!this.state.fontsLoaded) {
return <AppLoading />;
}
return (
<View style={styles.container}>
{/* Todays Tasks */}
<View style={styles.taskWrapper}>
<Text style={styles.sectionTitle}>Today's Tasks</Text>
<View style={styles.items}>
{/* This is where the tasks will go! */}
<FlatList
data={this.state.taskItems}
keyExtractor={(item) => item}
renderItem={({ item, index }) => (
<Task text={item} handleDelete={() => this.deleteItem(index)} />
)}
/>
</View>
</View>
{/* Write a Task */}
<KeyboardAvoidingView style={styles.writeTaskWrapper}>
<TextInput
style={styles.input}
placeholder={'Write A Task!'}
onChangeText={(text) => {
this.setState({ task: text });
}}
value={this.state.task}
/>
<TouchableOpacity
onPress={() => {
this.handleAddTask();
}}>
<View style={styles.addWrapper}>
<Text style={styles.addText}>+</Text>
</View>
</TouchableOpacity>
</KeyboardAvoidingView>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#E8EAED',
},
taskWrapper: {
paddingTop: 80,
paddingHorizontal: 20,
},
sectionTitle: {
fontSize: 24,
backgroundColor: '#fff',
fontFamily: 'Poppins_SemiBold',
borderRadius: 10,
margin: 'auto',
width: 250,
height: 60,
textAlign: 'center',
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.2,
shadowRadius: 2,
elevation: 5,
paddingTop: 10,
},
items: {
marginTop: 30,
},
writeTaskWrapper: {
position: 'absolute',
bottom: 60,
width: '100%',
flexDirection: 'row',
justifyContent: 'space-around',
alignItems: 'center',
},
input: {
paddingVertical: 15,
paddingHorizontal: 15,
backgroundColor: '#fff',
borderRadius: 60,
width: 250,
fontFamily: 'Poppins_Regular',
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.5,
shadowRadius: 2,
elevation: 3,
},
addWrapper: {
width: 60,
height: 60,
backgroundColor: '#fff',
borderRadius: 60,
justifyContent: 'center',
alignItems: 'center',
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.5,
shadowRadius: 2,
elevation: 3,
},
addText: {},
});
and this is my task.js code which is a component:
import React from 'react';
import {
View,
Text,
StyleSheet,
Dimensions,
Animated,
TouchableOpacity,
} from 'react-native';
import AppLoading from 'expo-app-loading';
import {
Poppins_100Thin,
Poppins_100Thin_Italic,
Poppins_200ExtraLight,
Poppins_200ExtraLight_Italic,
Poppins_300Light,
Poppins_300Light_Italic,
Poppins_400Regular,
Poppins_400Regular_Italic,
Poppins_500Medium,
Poppins_500Medium_Italic,
Poppins_600SemiBold,
Poppins_600SemiBold_Italic,
Poppins_700Bold,
Poppins_700Bold_Italic,
Poppins_800ExtraBold,
Poppins_800ExtraBold_Italic,
Poppins_900Black,
Poppins_900Black_Italic,
} from '#expo-google-fonts/poppins';
import { useFonts } from 'expo-font';
import Swipeable from 'react-native-gesture-handler/Swipeable';
const SCREEN_WIDTH = Dimensions.get('window').width;
const Task = (props) => {
let [fontsLoaded, error] = useFonts({
Poppins_100Thin,
Poppins_100Thin_Italic,
Poppins_200ExtraLight,
Poppins_200ExtraLight_Italic,
Poppins_300Light,
Poppins_300Light_Italic,
Poppins_400Regular,
Poppins_400Regular_Italic,
Poppins_500Medium,
Poppins_500Medium_Italic,
Poppins_600SemiBold,
Poppins_600SemiBold_Italic,
Poppins_700Bold,
Poppins_700Bold_Italic,
Poppins_800ExtraBold,
Poppins_800ExtraBold_Italic,
Poppins_900Black,
Poppins_900Black_Italic,
});
if (!fontsLoaded) {
return <AppLoading />;
}
const leftSwipe = (progress, dragX) => {
const scale = dragX.interpolate({
inputRange: [0, 100],
outputRange: [0, 1],
extrapolate: 'clamp',
});
return (
<TouchableOpacity onPress={props.handleDelete} activeOpacity={0.6}>
<View style={styles.deleteBox}>
<Animated.Text
style={{
transform: [{ scale: scale }],
color: '#fff',
fontFamily: 'Poppins_400Regular',
fontSize: 18,
}}>
Delete
</Animated.Text>
</View>
</TouchableOpacity>
);
};
return (
<Swipeable renderLeftActions={leftSwipe}>
<View style={styles.item}>
<View style={styles.itemLeft}>
<View style={styles.square}></View>
<Text style={styles.itemText}>{props.text}</Text>
</View>
<View style={styles.circular}></View>
</View>
</Swipeable>
);
};
const styles = StyleSheet.create({
item: {
backgroundColor: 'white',
padding: 15,
borderRadius: 10,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
marginBottom: 20,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.5,
shadowRadius: 2,
elevation: 3,
},
itemLeft: {
flexDirection: 'row',
alignItems: 'center',
flexWrap: 'wrap',
},
square: {
width: 24,
height: 24,
backgroundColor: '#55BCF6',
opacity: 0.5,
borderRadius: 5,
marginRight: 15,
},
itemText: {
maxWidth: '80%',
fontFamily: 'Poppins_400Regular',
},
circular: {
width: 12,
height: 12,
borderColor: '#55BCF6',
borderWidth: 2,
borderRadius: 5,
},
deleteBox: {
backgroundColor: 'red',
justifyContent: 'center',
alignItems: 'center',
width: 100,
height: 55,
borderRadius: 10,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.5,
shadowRadius: 2,
elevation: 5,
},
});
export default Task;
Due to React internal state update implementation, update Async storage run before new state updated. It's recommended to run asynchronous code before updating the state.
To solve the issue, refactor your code as below:
handleAddTask= async ()=>{
Keyboard.dismiss()
const updatedTaskItems = [...this.state.taskItems,this.state.task]
await AsyncStorage.setItem('data',JSON.stringify(updatedTaskItem))
this.setState({taskItems:updatedTaskUtems,task:''})
}
I am learning react native for the first time for developing android app not IOS one and I was learning from Building a Simple ToDo App With React Native and Firebase tutorial from youtube.But I am getting an error while trying to add items. I get following error
Firebase.push failed: first argument contains an invalid key($$typeof) in property 'restaurant.restaurant_name.targetInst._nativeParent._currentElement'.keys must be non-empty strings and cant contain ".","#","$","/","["
What might be creating this issue?
import React, { Component } from 'react';
import Firebase from 'firebase';
import {
AppRegistry,
StyleSheet,
Text,
View,
TouchableHighlight,
TextInput,
ListView
} from 'react-native';
class foodOrderSystem extends Component {
constructor(props){
super(props);
const firebaseRef = new Firebase('foodordersystem.firebaseio.com');
console.log(firebaseRef);
this.itemRef = firebaseRef.child('restaurant');
this.state = {
newRestaurant:'',
// estimated_delivery:'',
// description:'',
restaurantSource: new ListView.DataSource({rowHasChanged: (row1,row2) => row1 !== row2})
};
this.restaurant = [ ];
}
componentDidMount() {
this.itemRef.on('child_added', (snap) => {
this.restaurant.push({
id:snap.key(),
text:snap.val()
});
this.setState({
restaurantSource:this.state.restaurantSource.cloneWithRows(this.restaurant)
});
});
this.itemRef.on('child_removed', (snap) => {
this.restaurant = this.restaurant.filter((x) => x.id !== snap.key());
this.setState({
restaurantSource:this.state.restaurantSource.cloneWithRows(this.restaurant)
});
});
}
addRestaurant(){
if(this.state.newRestaurant !== ''){
this.itemRef.push({
restaurant_name: this.state.newRestaurant
});
this.setState({ newRestaurant:''});
}
}
removeRestaurant(rowData){
this.itemRef.child(rowData.id).remove();
}
render() {
return (
<View style={styles.appContainer}>
<View style={styles.titleView}>
<Text style={styles.titleText}>
foodOrderSystem
</Text>
</View>
<View style={styles.inputcontainer}>
<TextInput
style={styles.input}
onChange={(text) => this.setState({newRestaurant:text})}
value={this.state.newRestaurant} />
<TouchableHighlight
style={styles.button}
onPress={ () => this.addRestaurant() }
underlayColor="#00ffff">
<Text style={styles.btnText}>Add</Text>
</TouchableHighlight>
</View>
<ListView
dataSource={this.state.restaurantSource}
renderRow={this.renderRow.bind(this)} />
</View>
);
}
renderRow(rowData){
return(
<TouchableHighlight
underlayColor="#dddddd"
onPress={() => this.removeRestaurant(rowData)}>
<View>
<View style={styles.row}>
<Text style={styles.todoText}>{rowData.text}</Text>
</View>
<View style={styles.separator} />
</View>
</TouchableHighlight>
);
}
}
const styles = StyleSheet.create({
appContainer:{
flex: 1
},
titleView:{
backgroundColor: '#48afdb',
paddingTop: 30,
paddingBottom: 10,
flexDirection: 'row'
},
titleText:{
color: '#fff',
textAlign: 'center',
fontWeight: 'bold',
flex: 1,
fontSize: 20,
},
inputcontainer: {
marginTop: 5,
padding: 10,
flexDirection: 'row'
},
button: {
height: 36,
flex: 2,
flexDirection: 'row',
backgroundColor: '#48afdb',
justifyContent: 'center',
color: '#FFFFFF',
borderRadius: 4,
},
btnText: {
fontSize: 18,
color: '#fff',
marginTop: 6,
},
input: {
height: 36,
padding: 4,
marginRight: 5,
flex: 4,
fontSize: 18,
borderWidth: 1,
borderColor: '#48afdb',
borderRadius: 4,
color: '#48BBEC'
},
row: {
flexDirection: 'row',
padding: 12,
height: 44
},
separator: {
height: 1,
backgroundColor: '#CCCCCC',
},
todoText: {
flex: 1,
},
});
AppRegistry.registerComponent('foodOrderSystem', () => foodOrderSystem);
Here is the attachment of error