I'm making a react native app on Expo, but the whole app crashes everytime I try to get into this one page. This is my first time using react native, and I'm still a beginner to react, but I can't see what's causing the issue. The error I'm getting is Warning: Functions are not valid as a React child. This may happen if you return a Component instead of <Component /> from render. Or maybe you meant to call this function rather than return it., but I can't see where
import { StyleSheet, Text, View, TouchableOpacity, ScrollView, TextInput } from 'react-native'
import React, { useState, useEffect } from 'react'
// import CartCard from '../Components/CartCard'
import ItemCard from '../Components/ItemCard'
const Cart = ({navigation}) => {
const [details, setDetails] = useState([])
useEffect(()=>{
getData()
}, [])
const getData = async() => {
try {
const getValues = await AsyncStorage.getItem('object')
if(getValues !== null){
console.log(JSON.parse(getValues))
setDetails(getValues)
// const objectValues = JSON.parse(getValues)
// setDetails(objectValues)
// getValues.forEach(object => {
// console.log("id:", object.id)
// setToyId(object.id)
// })
}
}catch(error){
console.log('getData didnt work')
}
}
const [cardholder, setCardholder] = useState('');
const [cardNumber, setCardNumber] = useState('');
const [expiryDate, setExpiryDate] = useState('');
const [cvc, setCvc] = useState('');
const [allItems, setAllItems] = useState(0);
const [total, setTotal] = useState(0)
const clearCart = () => {
console.log('cc')
}
const countAllItems = () => {
console.log('cai')
}
const countTotal = () => {
console.log('ct')
}
return (
<ScrollView style={{backgroundColor: 'white', height: '100%'}}>
<ItemCard />
<View>
<View style={styles.payment}>
<Text>Your Shopping Cart</Text>
<View>
<Text value={allItems} onChangeText={(value)=>{setAllItems(value)}}>Overall Items: {countAllItems}</Text>
<Text value={total} onChangeText={(value)=>{setTotal(value)}}>Total Price: ${countTotal}.00</Text>
</View>
</View>
<Text>Payment</Text>
<TextInput placeholder='Cardholder Name' style={styles.inputsLong} placeholderTextColor='black' value={cardholder} onChangeText={(value)=>setCardholder(value)}/>
<TextInput placeholder='Card Number' style={styles.inputsLong} placeholderTextColor='black' value={cardNumber} onChangeText={(value)=>setCardNumber(value)}/>
<View style={styles.shortForm}>
<TextInput placeholder='MM/YY' style={styles.inputsShort} placeholderTextColor='black' value={expiryDate} onChangeText={(value)=>setExpiryDate(value)} />
<TextInput placeholder='CVC' style={styles.inputsShort} placeholderTextColor='black' value={cvc} onChangeText={(value)=>setCvc(value)}/>
</View>
<View style={styles.buttonRow}>
<TouchableOpacity>
<Text style={styles.cancelButton} onPress={clearCart}>Cancel Order</Text>
</TouchableOpacity>
<TouchableOpacity>
<Text style={styles.orderButton} onPress={()=>navigation.navigate('ConfirmScreen')}>Place Order</Text>
</TouchableOpacity>
</View>
</View>
</ScrollView>
)
}
export default Cart
const styles = StyleSheet.create({ // styles })
ItemCard component doesn't take any parameters yet because I'm having problems storing data and passing data between pages, but this is what the component looks like in its current state:
import { StyleSheet, Text, View, ScrollView } from 'react-native'
import React from 'react'
const ItemCard = () => {
return (
<ScrollView>
<View style={styles.itemSection}>
<View style={styles.card}></View>
</View>
</ScrollView>
)
}
export default ItemCard
const styles = StyleSheet.create({ // styles })
For this warning
Warning: Functions are not valid as a React child. This may happen if you return a Component instead of from render. Or maybe you meant to call this function rather than return it.
you have to call the functions
eg. countTotal()
<Text value={total} onChangeText={(value)=>{setTotal(value)}}>Total Price: ${countTotal()}.00</Text>
if you are using onPress then like this
<Text style={styles.cancelButton} onPress={()=>clearCart()}>Cancel Order</Text>
Related
I've been trying to replace a view in React Native, but to no success. The app closes without errors whenever I try <TouchableOpacity onPress={() => {handleChangeMyView();}}> :
What am I doing wrong? How can I make it work?
Thank you all in advance.
import React, {
useState
} from 'react';
import {
SafeAreaView,
View,
TouchableOpacity,
} from 'react-native';
import MyInitialView from './MyInitialView';
const SiteContainer = () => {
let MyDynamicView = () => {
return (
<View></View>
);
};
const [MyDynamicViewArea, setMyDynamicViewArea] = useState(MyInitialView);
const handleChangeMyView = () => {
setMyDynamicViewArea(MyDynamicView);
};
return (
<SafeAreaView>
{MyDynamicViewArea}
<TouchableOpacity onPress={() => {handleChangeMyView();}}>
<View>
<FontAwesome name="quote-left"></FontAwesome>
</View>
</TouchableOpacity>
</SafeAreaView>
);
};
export default SiteContainer;
MyInitialView :
import React from 'react';
import {
View
} from 'react-native';
export default function MyInitialView() {
return (
<View></View>
);
}
You can use boolean value for viewing MyInitialView using useState
const [toViewMyInitialView, setToViewMyInitialView] = useState(false);
and in handleChangeMyView function set the above value as true
const handleChangeMyView = () => {
setToViewMyInitialView(true);
};
And in the SiteContainer
<SafeAreaView style={styles.siteContainer}>
// here I don't know a way to display a component in react-native, so
// you need to display the component MyInitialView if the
// toViewMyInitialView is true and when toViewMyInitialView id false it
// display MyDynamicViewArea
{toViewMyInitialView && <MyInitialView/>}
{!toViewMyInitialView && <MyDynamicViewArea/>}
<TouchableOpacity onPress={() => {handleChangeMyView()}}>
<View>
<FontAwesome name="quote-left"></FontAwesome>
</View>
</TouchableOpacity>
</SafeAreaView>
I'm building a React Native gig guide app, using stack navigation to get around the different screens.
So here's what I want to happen - the user can navigate from Map.js to List.js, where they're presented with a list of gigs. I want them to be able to tap on a gig and be directed to GigDetails.js, where they can see the details about that specific gig.
Users also have the option of tapping on a gig marker in Map.js which also directs them to GigDetails.js. How do I configure my routing so users can get to GigDetails from both Map.js and List.js?
Currently, Map-to-details is working, but I don't know how to go from List-to-details.
App structure is as follows (route with asterisk is the part I'm having trouble with:
screens/List.js <-----> screens/Map.js (Home screen)
*| |
*| | (GigDetails navigated to via map Marker Callout)
*| |
*GigDetails.js GigDetails.js
Code:
App.js
import 'react-native-gesture-handler';
import { StyleSheet, Text, View } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { MyStack } from './routes/homeStack';
export default function App() {
return (
<NavigationContainer>
<MyStack/>
</NavigationContainer>
);
}
HomeStack.js
import { createStackNavigator } from "#react-navigation/stack";
import List from '../screens/List'
import Map from '../screens/Map'
import Header from "../components/Header";
import GigDetails from "../screens/GigDetails";
const Stack = createStackNavigator()
export const MyStack = () => {
return (
<Stack.Navigator
initialRouteName="Map"
>
<Stack.Screen
name="Map"
component={Map}
options={{
headerTitle: () => <Header/>,
headerTitleAlign: 'center'
}}
/>
<Stack.Screen
name="List"
component={List}
options={{
headerTitle: () => <Header/>,
headerTitleAlign: 'center'
}}
/>
<Stack.Screen
name="GigDetails"
component={GigDetails}
options={{
headerTitle: () => <Header/>,
headerTitleAlign: 'center'
}}
/>
</Stack.Navigator>
);
};
Map.js
import { StyleSheet,View,Text,Pressable } from 'react-native'
import GigMap from '../components/GigMap'
const Map = ({ navigation }) => {
return (
<View style = {styles.container}>
<GigMap navigation = {navigation}/>
<View style = {styles.footer}>
<Pressable
title = "Go to list view"
onPress = {() => navigation.navigate("List")}
style = {styles.button}
>
<Text style = {styles.buttonText}>List View</Text>
</Pressable>
</View>
</View>
)
}
GigMap.js
import { useState,useEffect } from 'react';
import { StyleSheet, Text, View,Pressable } from 'react-native';
import MapView from 'react-native-maps';
import { Marker,Callout } from 'react-native-maps';
import { query,collection,getDocs } from 'firebase/firestore';
import { db } from '../firebase';
import CalloutView from './CalloutView';
import { mapStyle } from '../util/mapStyle';
import dayjs from 'dayjs';
const GigMap = ({ navigation }) => {
const [gigs, setGigs] = useState([]);
const [ date,setDate ] = useState(dateToday)
const [ daysAdded,setDaysAdded ] = useState(1)
//Generating current date
const addHours =(numOfHours, date = new Date()) => {
date.setTime(date.getTime() + numOfHours * 60 * 60 * 1000);
return date;
}
let localDate = addHours(13)
useEffect(() => {
setDate(localDate)
},[])
const addDay = () => {
setDaysAdded(daysAdded+1)
localDate.setDate(localDate.getDate() + daysAdded)
setDate(localDate)
}
console.log(date)
const day = new Date().getDate();
const month = new Date().getMonth() + 1;
const year = new Date().getFullYear();
const dateToday = `${day}/${month}/${year}`;
//Making call to Firebase to retrieve gig documents from 'gigs' collection
useEffect(() => {
const getGigs = async () => {
try {
const gigArray = [];
const q = query(collection(db, "gigs"));
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) =>
gigArray.push({ id: doc.id, ...doc.data() })
);
setGigs(gigArray);
} catch (err) {
console.log(`Error: ${err}`);
}
};
getGigs();
}, []);
//Filtering through gigs to return only current day's gigs
const gigsToday = gigs.filter((gig) => gig.date === dateToday);
return (
<View style={styles.container}>
<Text style={styles.headerText}>Today's gigs</Text>
<MapView
initialRegion={{
latitude: -41.29416,
longitude: 174.77782,
latitudeDelta: 0.03,
longitudeDelta: 0.03,
}}
style={styles.map}
customMapStyle={mapStyle}
>
{gigsToday.map((gig, i) => (
<Marker
key={i}
coordinate={{
latitude: gig.location.latitude,
longitude: gig.location.longitude,
}}
image={require("../assets/Icon_Gold_48x48.png")}
>
<Callout
style={styles.callout}
onPress={() =>
navigation.navigate("GigDetails", {
venue: gig.venue,
date: gig.date,
gigName: gig.gigName,
time: gig.time,
})
}
>
<CalloutView
venue={gig.venue}
date={gig.date}
gigName={gig.gigName}
time={gig.time}
style={styles.calloutView}
/>
</Callout>
</Marker>
))}
</MapView>
<View style = {styles.buttonOptions}>
<Pressable >
<Text style = {styles.buttonOptionsText}>previous day's gigs</Text>
</Pressable>
<Pressable onPress = {addDay}>
<Text style = {styles.buttonOptionsText}>next day's gigs</Text>
</Pressable>
</View>
</View>
);
};
List.js
import { View,Text } from 'react-native'
import ListByDay from '../components/ListByDay';
const List = () => {
return (
<View>
<ListByDay/>
</View>
)
}
export default List;
ListByDay.js
import { StyleSheet, Text, View,FlatList,TouchableOpacity } from 'react-native';
import { useGigs } from '../hooks/useGigs';
const ListByDay = ({ navigation }) => {
const gigs = useGigs()
return (
<View>
<Text style = {styles.header}>Gigs today</Text>
<FlatList
data = {gigs}
renderItem = {({ item }) => (
<TouchableOpacity style = {styles.test}>
<Text>{item.venue}</Text>
<Text>{item.gigName}</Text>
<Text>{item.date}</Text>
<Text>{item.time}</Text>
</TouchableOpacity>
)}
/>
</View>
);
}
const styles = StyleSheet.create({
test: {
borderWidth:1,
borderColor:'black',
},
header: {
padding:10
}
})
export default ListByDay;
The component ListByDay currently tries to destructure the navigation object from its props. However, the navigation object is passed by the react-navigation framework to the components that are defined as screens inside a navigator. This is not the case for ListByDay. Thus, you cannot access the navigation object as you are currently trying to do.
There are multiple options.
1) Pass the navigation object as a prop from its parent which is a screen inside a navigator
Since List is the parent component for ListByDay and List is an actual screen defined in your stack navigator, it will receive the navigation object as a prop from the react-navigation framework. You can access it and pass it to its children.
const List = ({navigation}) => {
return (
<View>
<ListByDay navigation={navigation} />
</View>
)
}
Then, implement the navigation logic in the onPress function of your TouchableOpacity inside ListByDay.
const ListByDay = ({ navigation }) => {
const gigs = useGigs()
return (
<View>
<Text style = {styles.header}>Gigs today</Text>
<FlatList
data = {gigs}
renderItem = {({ item }) => (
<TouchableOpacity style = {styles.test} onPress={() => navigation.navigate(...)}>
<Text>{item.venue}</Text>
<Text>{item.gigName}</Text>
<Text>{item.date}</Text>
<Text>{item.time}</Text>
</TouchableOpacity>
)}
/>
</View>
);
}
2) Use the useNavigation hook
If you do not want to pass the navigation object down to ListByDay, then you can always use the useNavigation hook instead.
const ListByDay = () => {
const navigation = useNavigation()
const gigs = useGigs()
return (
<View>
<Text style = {styles.header}>Gigs today</Text>
<FlatList
data = {gigs}
renderItem = {({ item }) => (
<TouchableOpacity style = {styles.test} onPress={() => navigation.navigate(...)}>
<Text>{item.venue}</Text>
<Text>{item.gigName}</Text>
<Text>{item.date}</Text>
<Text>{item.time}</Text>
</TouchableOpacity>
)}
/>
</View>
);
}
I have no errors showing but I am getting a blank page when my app runs. It started rendering a blank page when started putting some api code which I copied and pasted from another project where it worked exactly perfect but for some reason, it is refusing to work here This is how my code looks like. This is my App.js file
import React from 'react'
import { StyleSheet, Text, View } from 'react-native'
import Screen from './app/components/Screen'
import ProductScreen from './app/screens/ProductScreen';
export default function App() {
return (
<Screen>
<ProductScreen />
</Screen>
)
}
Then this is the product screen.js
import React, {useState, useEffect} from 'react'
import { FlatList, StyleSheet, ActivityIndicator, Text, View } from 'react-native'
import Card from '../components/Card';
export default function ProductScreen() {
const [products, setproducts] = useState([]);
const [loading, setloading] = useState(true);
const getProducts = async () => {
try {
const response = await fetch('https://fakestoreapi.com/products/1');
const data = await response.json();
setproducts(data);
} catch (error) {
console.log("Something went wrong in your code",error)
} finally {
setloading(false);
}
}
useEffect(() => {
getProducts();
}, []);
return (
<View>
{loading ? <ActivityIndicator/> : (
<FlatList
data={products}
keyExtractor={(id) => id}
renderItem={({item}) => (
<Card
title={item.title}
subtitle={item.description}
img={item.image}
/>
)}
/>
)}
</View>
)
}
const styles = StyleSheet.create({})
And lastly the card.js file
import { Image, StyleSheet, Text, TouchableOpacity, View } from 'react-native'
import AppText from './AppText';
export default function Card({title, subtitle, img}) {
return (
<View style={styles.container}>
<Image source={img} style={styles.image} />
<View style={styles.cardText}>
<AppText style={{color: "black"}}>{title}</AppText>
<AppText style={{color: "#4ecdc4"}}>{subtitle}</AppText>
</View>
</View>
)
}
Where could I be going wrong?
Well there are several issues in your Expo Snack. First your expression of:
{loading && <FlatList />}
is wrong because you're setting it to check for loading of true when after you retrieve your data in getProducts you set it to false.
Was able to get it to work, again, with:
import React, { useState, useEffect } from 'react'
import { FlatList, StyleSheet, View } from 'react-native'
import Card from '../components/Card'
export default function ProductScreen() {
const [products, setProducts] = useState([])
const [loading, setLoading] = useState(true)
const getProducts = async () => {
try {
const response = await fetch('https://fakestoreapi.com/products/5')
const data = await response.json()
setProducts([data])
setLoading(false)
} catch (error) {
console.log('Something went wrong in your code', error)
}
}
useEffect(() => {
getProducts()
}, [])
return (
<View style={styles.container}>
{!loading && products.length !== 0 && (
<View>
<FlatList
data={products}
keyExtractor={(_, id) => id.toString()}
renderItem={({ item }) => <Card title={item.title} subtitle={item.description} img={item.image} />}
/>
</View>
)}
</View>
)
}
const styles = StyleSheet.create({
container: {
backgroundColor: 'lightgrey',
flex: 1,
},
})
Another error exists in Card for Image. Per memory when passing an image URL it should be set with a URI:
<Image source={{ uri: img }} style={styles.image} />
Another issue you'll face is in ProductScreen the View style for container should have a flex: 1 applied as indicated in the code above.
Looking at the code for snack.expo.dev/#vuyi/spontaneous-candy it seems your missing the conditional rendering in your return for the ProductScreen.
Your code looks like this:
<View style={styles.container}>
{loading &&
<FlatList
data={[products]}
keyExtractor={(_, id) => id.toString()}
renderItem={({item}) =>
<Card
title={item.title}
subtitle={item.description}
img={item.image}
/>
}
/>
}
</View>
When it needs to look something like this:
<View style={styles.container}>
{loading ? null :
<FlatList
data={[products]}
keyExtractor={(_, id) => id.toString()}
renderItem={({item}) =>
<Card
title={item.title}
subtitle={item.description}
img={item.image}
/>
}
/>
}
</View>
Adding that conditional will prevent your Flatlist component from rendering before it has access to the data you are fetching. It looks like you had it right in the original post but not in the Expo Snack environment.
I am trying to run a react-native-web project, when I try running it, I get the following error
When I stacktraced I found my error is exactly happening in the following code
import React, {useState, useEffect} from 'react';
import {
SafeAreaView,
KeyboardAvoidingView,
TextInput,
View,
Text,
Image,
Button,
TouchableOpacity,
} from 'react-native';
import qs from 'qs';
import {Images} from '../../assets';
import {API} from '../../api';
import {disabledColor, successColor} from '../../config/colors';
export default function LoginScreenWeb(props) {
const {navigation} = props;
const [firstName, setFirstName] = useState('');
const [mobile, setMobile] = useState('');
const [width, getWidth] = useState(null);
const SetWidth = () => {
getWidth(window.screen.availWidth);
};
// useEffect(() => {
// // SetWidth();
// // setInterval(SetWidth, 1000);
// }, []);
// const onHandlePress = () => {
// async function post() {
// try {
// const response = await API.postFormData(
// `/users/signup`,
// qs.stringify({
// firstName,
// mobile,
// }),
// );
// navigation.navigate('otp');
// localStorage.setItem('mobile', mobile);
// console.log('loginRes--', response);
// } catch (e) {
// console.log(e);
// }
// }
// post();
// };
return (
<SafeAreaView style={styles.container}>
<View
// style={
// width > 414 ? styles.inner_container_web : styles.inner_container_mob
// }
>
<Image
source={Images.login_web}
// style={width > 414 ? styles.image_web : styles.image_mob}
/>
<KeyboardAvoidingView>
<View style={{padding: 16}}>
<Text style={styles.textLabel}>Name</Text>
<TextInput
placeholder="Enter your name"
style={styles.textInput}
// value={firstName}
// onChangeText={(firstName) =>
// setFirstName(firstName.replace(/[^A-Za-z]/g, ''))
// }
/>
<Text style={styles.textLabel}>+91</Text>
<TextInput
placeholder="Enter your phone number"
style={styles.textInput}
maxLength={10}
keyboardType="number-pad"
// value={mobile}
// onChangeText={(mobile) =>
// setMobile(mobile.replace(/[^0-9]/g, ''))
// }
/>
<View style={{alignItems: 'center'}}>
{/* {firstName && mobile.length === 10 ? ( */}
<TouchableOpacity
// onPress={() => onHandlePress()}
style={styles.enable_continue}>
<Text style={styles.continue}>Continue</Text>
</TouchableOpacity>
{/* ) : ( */}
<TouchableOpacity style={styles.disble_continue}>
<Text style={styles.continue}>Continue</Text>
</TouchableOpacity>
{/* )} */}
</View>
<View style={{flexDirection: 'row'}}>
<View style={styles.cookies} />
<Text style={{fontSize: '12px'}}>
By continuing you agree to the Terms of Service and Privacy
policy
</Text>
</View>
</View>
</KeyboardAvoidingView>
</View>
</SafeAreaView>
);
}
As soon as I uncomment the useeffect part I get the Hooks can only be called inside the body of a function component. error however I am using a functional component here.
Am unable to figure how to fix the error, could anyone help me out here, thanks in advance, let me know if anything else is required for clarity
Stacktrace ->
try this too :
const LoginScreenWeb= (props) => {//////another code}
export default LoginScreenWeb
Also check the version(react) you are working with..
React Hooks are available in React 16.8
This was an error of npm link and version mismatch, once I linked my react package the error got fixed
import { StyleSheet, Text, View } from 'react-native'
import * as firebase from 'firebase';
//firebaseinititalized//
const[message,setMessage]=useState('');
const [messages, setMessages] = useState([])
useEffect(() => {
firebase.database.ref().child('messages').once('value',
snapshot=>{
const data=snapshot.val()
if(snapshot.val()){
const initMessages=[];
Object.
keys(data).
forEach(message=>initMessages.push(data[message]));
(initMessages)=>setMessages([initMessages]);
}
}
)
firebase.database.ref().child('messages').on("child_added",
snapshot=>{
const data=snapshot.val()
if(snapshot.val()){
(prevMessages)=> setMessages([data,...prevMessages]);
}
}
)
}, [])
const addItem =()=>{
if(!message) return;
const newMessage=firebase.database.ref().child().push();
newMessage.set(message,()=>setMessage(''));
}
function Hookfire() {
return (
<View >
<View >
<TextInput placeholder=" enter text message"
value={message} onChangeText={text=>setMessage(text)}/>
<Button title="send" onPress={addItem}/>
</View>
<FlatList data={messages}
renderItem={({item})=>
<View >
<Text >{item}</Text>
</View>}
/>
</View>
)
}
export default Hookfire
const styles = StyleSheet.create({})
error message shows that:
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
you have move the hook call inside the HookFire function.
import { StyleSheet, Text, View } from 'react-native'
import * as firebase from 'firebase';
function Hookfire() {
const[message,setMessage]=useState('');
const [messages, setMessages] = useState([])
useEffect(() => {
firebase.database.ref().child('messages').once('value',
snapshot=>{
const data=snapshot.val()
if(snapshot.val()){
const initMessages=[];
Object.
keys(data).
forEach(message=>initMessages.push(data[message]));
(initMessages)=>setMessages([initMessages]);
}
}
)
firebase.database.ref().child('messages').on("child_added",
snapshot=>{
const data=snapshot.val()
if(snapshot.val()){
(prevMessages)=> setMessages([data,...prevMessages]);
}
}
)
}, [])
const addItem =()=>{
if(!message) return;
const newMessage=firebase.database.ref().child().push();
newMessage.set(message,()=>setMessage(''));
}
return (
<View >
<View >
<TextInput placeholder=" enter text message"
value={message} onChangeText={text=>setMessage(text)}/>
<Button title="send" onPress={addItem}/>
</View>
<FlatList data={messages}
renderItem={({item})=>
<View >
<Text >{item}</Text>
</View>}
/>
</View>
)
}
export default Hookfire;