How can pass state variable between components REACT NATIVE - reactjs

I have 2 components A and B.
How can pass a variable from component A to component B ?
ComponentA.js
const ComponentA = () => {
//INSET
const insets = useSafeAreaInsets();
//GET HEADER HEIGHT
const [heightHeader, setHeightHeader] = useState(false)
return (
<View
onLayout={({ nativeEvent }) => {
const { height } = nativeEvent.layout
setHeightHeader(height)
}}>
</View>
)
}
export default ComponentA
I want to get from const [heightHeader, setHeightHeader] = useState(false)
the heightHeader variable
ComponentB.js
import ComponentA from './ComponentA';
const ComponentB = () => {
return (
<View style={{
flex:1
}}>
<View style={{
flex:1,
paddingTop: heightHeader,
}}>
</View>
</View>
)
}
export default ComponentB
I want to get heightHeader variable from ComponentA to ComponentB.
*The 2 Components isn't in the same file

Use redux for maintaining global state.
OR
Use pass props feature provided by React Navigation like this :
function HomeScreen({ navigation: { navigate } }) {
return (
<View>
<Text>This is the home screen of the app</Text>
<Button
onPress={() =>
navigate('Profile', {heightHeader:heightHeader })
}
title="Go to Brent's profile"
/>
</View>
);
}
For more follow this link

Related

React native page keeps crashing, I can't see why

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>

Confusion with React Native Navigation

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>
);
}

Calling a custom hook with a parameter from a custom component

I have this custom hook I want to be executed when pressing a button inside a custom component
import { useDispatch } from 'react-redux';
import { useCallback } from 'react';
import { retireUserFromEvents } from '../../../redux/actions/Events';
export default function useDeleteAssistantToEvent() {
const dispatch = useDispatch();
const deleteAssistantToEvent = useCallback(userID => {
dispatch(retireUserFromEvents(userID));
}, [dispatch]);
return deleteAssistantToEvent;
}
I declare the hook on the screen where the custom component is:
import {
AttendantList
} from '../../../../components';
import useDeleteAssistantToEvent from '../../../../hooks/assistance/useDeleteAssistantToEvent';
export default function EventAssistantsView({ navigation }) {
const deleteAssistant = useDeleteAssistantToEvent();
return (
<SafeAreaProvider>
<SafeAreaView style={styles.safeAreaViewStyle}>
<View style={styles.mainViewStyle}>
<AttendantList
...
undoInvite={() => deleteAssistant}
/>
</View>
</SafeAreaView>
</SafeAreaProvider>
);
}
And this is my custom component (AttendantList):
export default function AttendantList({ attendees, goToUserProfile, undoInvite, imHostOrCohost }) {
return (
<View style={styles.mainViewStyle}>
{attendees.length > 0 ? (
<FlatList
...
renderItem={({ item }) => (
<TouchableOpacity onPress={goToUserProfile()}>
<InviteCard
...
onPress={() => undoInvite(item.id)}
/>
</TouchableOpacity>
)}
/>
) : (
...
)}
</View>
);
}
ÌnviteCard is the button in question.
The problem is that, anytime I want to press the button, nothing happens. So, how can I pass the hook call to the custom component?

How can I create method of my own component in react-native?

For example, in react-native, I can pass a ref to a regular TextInput and then, through this ref, call the methods:
const inputRef = useRef(null);
const myCallback = () => {
inputRef?.current?.focus();
}
<>
<TouchableOpacity onPress={() => myCallback()}>
<Text>
Press here to focus the input!
</Text>
</TouchableOpacity>
<TextInput
ref={inputRef}
{...props} // Doesn't matter, nothing special
>
</>
So, my question is, how can I create methods on my components, so I can call them from outside of component using ref.
Of course, I'm interested in creating a method in a functional component.
You can use useImperativeHandle hook to expose the methods you need to have with the input element.
Try like this.
import React, { useImperativeHandle, forwardRef, useRef } from "react";
import { Button, StyleSheet, View, TextInput } from "react-native";
const MyTextInput = (props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
doFocus: () => {
inputRef.current.focus();
},
doBlur: () => {
inputRef.current.blur();
}
}));
return <TextInput ref={inputRef} style={props.style} />;
};
const MyCustomTextInput = forwardRef(MyTextInput);
const App = () => {
const myInputRef = useRef();
return (
<View style={styles.app}>
<MyCustomTextInput ref={myInputRef} style={styles.input} />
<View style={styles.button}>
<Button
onPress={() => {
myInputRef?.current?.doFocus();
}}
title="focus"
style={styles.button}
/>
</View>
<View style={styles.button}>
<Button
onPress={() => {
myInputRef?.current?.doBlur();
}}
title="blur"
style={styles.button}
/>
</View>
</View>
);
};
Code sandbox => https://codesandbox.io/s/react-native-web-forked-mnwee?file=/src/App.js

Problem with passing Params between Stack and Drawer Navigator screens - React-Native

Hi I am new to react native and trying to learn few things. I am trying to pass a data from one screen to another.
I need to pass the video id to Web View on another page to play YouTube video. but the Video Id is not passed to another screen.
I've tried to pass Param to one screen to another.In this project, I am using stack and drawer navigators.
The param id is "ytId"
also i tried to pass the param with AsyncStorage. Please anyone assist me with this issue and thanks in advance.
Screen 3:
import React from 'react';
import { Text, View, FlatList, Image, TouchableWithoutFeedback} from 'react-native';
import { Button, Icon } from 'native-base';
export default class App extends React.Component {
navOptions
static navigationOptions = ({ navigation }) => {
navOptions = navigation;
const { params = {} } = navigation.state;
return {
headerLeft: (
<Button
transparent
onPress={() => params._onHeaderEventControl()}
>
<Icon
name="menu"
style={{ fontSize: 30, color: 'white' }}
/>
</Button>
)
}
}
constructor(props) {
super(props);
this.state = { listLoaded: false };
}
onHeaderEventControl() {
const { params = {} } = navOptions.state;
params._openNav()
}
componentDidMount() {
this.props.navigation.setParams({
_onHeaderEventControl: this.onHeaderEventControl,
_openNav: () => this.openDrawer()
})
return fetch(
'https://www.googleapis.com/youtube/v3/search?part=snippet&q=lcwell&type=video&key=AIzaSyCwCHIfFvkMZ1aR6eIvy4sUIgqV6hIZ3qU')
.then((response) => response.json())
.then((responseJson) => {
this.setState({
listLoaded: true,
videoList: Array.from(responseJson.items)
})
})
.catch((error) => {
console.error(error);
});
}
openDrawer() {
this.props.navigation.openDrawer();
}
render() {
const { navigate } = this.props.navigation;
return (
<View>
{this.state.listLoaded && (
<View style={{ paddingTop: 0 }}>
<FlatList
data={this.state.videoList}
renderItem={({ item }) =>
<TubeItem
navigate={navigate}
id={item.id.videoId}
title={item.snippet.title}
imageSrc={item.snippet.thumbnails.high.url}
/>
}
keyExtractor={(item, index) => index.toString()}
/>
</View>
)}
{!this.state.listLoaded && (
<View style={{ paddingTop: 30 }}>
<Text>LOADING</Text>
</View>
)}
</View>
);
}
}
export class TubeItem extends React.Component {
onPress = () => {
this.props.navigate('Screen5', { ytId: this.props.id })
};
render() {
return (
<TouchableWithoutFeedback onPress={this.onPress}>
<View style={{ paddingTop: 20, alignItems: 'center' }}>
<Image
style={{ width: '100%', height: 200 }}
source={{ uri: this.props.imageSrc }}
/>
<Text>
{this.props.title}
</Text>
</View>
</TouchableWithoutFeedback>
);
}
}
Screen 5:
import React from 'react';
import { WebView } from 'react-native';
export default class VideoDetail extends React.Component {
navOptions
static navigationOptions = ({ navigation }) => {
navOptions = navigation;
const { params = {} } = navigation.state;
}
onHeaderEventControl() {
const { params = {} } = navOptions.state;
params._openNav()
}
componentDidMount() {
this.props.navigation.setParams({
_onHeaderEventControl: this.onHeaderEventControl,
_openNav: () => this.openDrawer()
})
}
render() {
let tubeId = this.props.navigation.getParam('ytId', 'NO VIDEO');
let tubeUrl = `https://www.youtube.com/embed/${tubeId}`;
return (
<WebView
style={{ marginTop: 20 }}
javaScriptEnabled={true}
source={{ uri: tubeUrl }}
/>
);
}
}
I would suggest you to use a state container like redux.
It allows you to pass variable and parameters from a component to another.
I didn't explain all in details, others do it better than me and there a re a lot of tutorials to implement redux.
You can find the official redux website https://redux.js.org/introduction/getting-started
Main steps would be:
Import redux in your package.json
Create a store with createStore method from imported package
Surround your main view with new created object <Provider store={store}>
Declare needed methods in your store
Connect Screen 3 & Screen 5 to redux
You will then be able to pass variable between your screens and access it very easy via props property.
It will simplify your life!
Otherwise we would need the way you declare your Stack and Drawer to be able to answer :-)

Resources