I am still trying to finish my first React Native app. There is another thing for which I cannot find a solution.
My application has different Screens (HomeScreen, NewsScreen, etc). Each of these screens uses the component Screen:
const Screen = ({props, children}) => {
console.log("Screen props: " + props);
return (
<>
<OfflineNotice />
<Header {...props} />
<SafeAreaView style={styles.screen}>
<View style={styles.container}>{ children }</View>
</SafeAreaView>
</>
);
}
export default Screen;
Each Screen component has another Header component (there is a dropdown, by choosing another option it will change the displayed information on the screen)
Here is the Header component:
export default function Header (props) {
console.log("Header props: " + props.loadPredictions);
const {sport, setSport} = useContext(AppContext);
const logo = config[sport].logo;
return (
<View style={styles.container}>
<View style={styles.link}>
<TouchableOpacity
onPress={() => props.navigate("Settings")}
>
<MaterialCommunityIcons name="tune" size={32} />
</TouchableOpacity>
</View>
<View style={styles.logo}>
<Image source={logo} style={styles.image} />
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Menu
ref={this.setMenuRef}
button={<MaterialCommunityIcons name="chevron-down" size={24} onPress={this.showMenu} />}
>
<MenuItem onPress={() => {changeSport('soccer', setSport, props)}}>{i18n.t('predictions.p_1')}</MenuItem>
<MenuItem onPress={() => {changeSport('basketball', setSport, props)}}>{i18n.t('predictions.p_3')}</MenuItem>
<MenuItem onPress={() => {changeSport('volleyball', setSport, props)}}>{i18n.t('predictions.p_4')}</MenuItem>
<MenuItem onPress={() => {changeSport('hockey', setSport, props)}}>{i18n.t('predictions.p_5')}</MenuItem>
<MenuItem onPress={() => {changeSport('tennis', setSport, props)}}>{i18n.t('predictions.p_6')}</MenuItem>
</Menu>
</View>
</View>
</View>
);
};
And here is my HomeScreen:
export default function HomeScreen({ navigation }) {
...
useEffect( () => {
loadPredictions(params);
}, []);
const loadPredictions = async (params) => {
...
}
return (
<Screen navi={navigation} loadPredictions={loadPredictions}>
<View style={styles.activity}>
<ActivityIndicator animating={loading} size="large" />
</View>
<View style={styles.main}>
...
</View>
</Screen>
);
}
On the HomeScreen I am passing through the Screen component two props: navi and loadPredictions. Unfortunately, I cannot access them. I am getting the error undefined.
What am I doing wrong?
Simply because you made a distructuring the props in Screen Component, thus if you console log the props you will find it undefined, the best approach that you do this:
const Screen = ({children, ...restProps}) => {
console.log("Screen props: " + restProps);
return (
<>
<OfflineNotice />
<Header {...restProps} />
<SafeAreaView style={styles.screen}>
<View style={styles.container}>{ children }</View>
</SafeAreaView>
</>
);
}
export default Screen;
In this way we will be able to access to children prop and you pass the rest of the Screen Props to what Component you want.
I hope this answer can help you out.
change this
const Screen = ({props, children})
to
const Screen = (props)
and access children props.children
Related
Im trying to implement this relatively popular bottom sheet in React Native by #gorhom.
My aim is to open the bottom sheet from another component. I've tried to follow this answer - here . However, i do not understand what goes in the touchable opacity onPress in my component where it is being called from.
Code below
BottomSheetModalComponent
export default function BottomSheetModalComponent({ref}) {
// ref
const bottomSheetModalRef = useRef<BottomSheetModal>(null);
// variables
const snapPoints = useMemo(() => ['25%', '50%'], []);
// callbacks
const handlePresentModalPress = useCallback(() => {
ref.current?.present();
}, []);
const handleSheetChanges = useCallback((index: number) => {
console.log('handleSheetChanges', index);
}, []);
// renders
return (
<BottomSheetModalProvider>
<View>
<BottomSheetModal
ref={ref}
snapPoints={snapPoints}
onChange={handleSheetChanges}
>
<View>
<Text>Awesome 🎉</Text>
</View>
</BottomSheetModal>
</View>
</BottomSheetModalProvider>
);
};
Location Component
export default function LocationBar() {
// Create Ref
const userBottomSheetRef = useRef<BottomSheetModal>(null);
// Pass ref into the bottom sheet component
<LocationBottomSheet ref={userBottomSheetRef} snapPoints={["30%"]}/>
return (
<>
<View style={{ flexDirection: "row" }}>
<View style={styles.locationBar}>
<Octicons style={styles.locationIcon} name="location" size={18} />
<TouchableOpacity onPress={() => {
//WHAT GOES HERE?
}}>
<Text style={{fontSize: 17, fontWeight: '600'}}>{userLocation}</Text>
</TouchableOpacity>
<Ionicons style={styles.chevronIcon} name="chevron-down" size={12} />
</View>
</View>
</>
);
}
Thanks in advance
I am trying to make drawer navigation from component. But all time its giving me error "TypeError: Cannot read property 'navigate' of undefined" when click on menu ->profile link. Please suggest where I am wrong.
component/MenuPage.js
import React from 'react';
import {SafeAreaView, View, StyleSheet, Image, Text, Linking,} from 'react-native';
import { DrawerContentScrollView, DrawerItemList, DrawerItem,} from '#react-navigation/drawer';
const image = { uri: "" };
const CustomSidebarMenu = (props) => {
return (
<SafeAreaView style={[styles.LandingBodyColor, {flex: 1, paddingHorizontal:10}]}>
<View style={[styles.LandingMain, {marginVertical:10,}]}>
<Image source={image} style={{width:100, height:100, borderRadius:100,}} />
</View>
<View>
<Text style={[styles.TextCenter, {fontSize:18, color: '#fff', paddingBottom:10,}]}>Welcome Back.</Text>
</View>
<DrawerContentScrollView {...props}>
<DrawerItemList {...props} />
<View style={[styles.customItem]}>
<Text style={{marginRight:10,}}>
<FontAwesome name="user" style={[styles.MenuIcon, styles.VectorIconWhiteColor]}/>
</Text>
<Text style={[styles.WhiteColor]} onPress={() => navigation.navigate(Profile) >My Profile</Text>
</View>
</DrawerContentScrollView>
</SafeAreaView>
);
};
export default CustomSidebarMenu;
App.js
export default function App() {
return (
<NavigationContainer>
<Drawer.Navigator
drawerContentOptions={{
activeBackgroundColor: '#e40046',
}}
drawerContent={(props) => <CustomSidebarMenu {...props} />}
screenOptions={{
headerShown: false,
}}
>
<Drawer.Screen name="Landing Page" component={AllScreenStack}
options={() => ({
drawerLabel: () => null,
title: undefined,
drawerIcon: () => null,
headerTitleStyle: {
backgroundColor:'red',
},
})}
/>
</Drawer.Navigator>
</NavigationContainer>
);
}
const Stack = createStackNavigator();
const Drawer = createDrawerNavigator();
Try with props like this
props.navigation.navigate('Profile')
hope someone would help me with this problem. I have create my own buttons and when I`m calling them as a component in a screen onPress navigation is not working, only in the Button Component from react native. This is my code:
Custom Button:
export const MediumButton = ({ title }) => {
return (
<TouchableOpacity style={styles.mediumButton}>
<Text style={styles.buttonText}>{title}</Text>
</TouchableOpacity>
);
};
And this is the code of screen that I`m calling the MediumButton:
import { MediumButton } from "../../components/Button/Button";
export default function Home({ navigation }) {
return (
<View style={global.container}>
<View style={styles.container}>
<Text style={styles.titleText}>Miresevini ne Hajde App</Text>
<MediumButton
title="Kycu"
onPress={() => navigation.navigate("Register")}
/>
<MediumButton
title="Regjistrohu"
onPress={() => navigation.navigate("Login")}
/>
</View>
</View>
);
}
You have to use the onPress prop that you are passing like below
export const MediumButton = ({ title,onPress }) => {
return (
<TouchableOpacity style={styles.mediumButton} onPress={onPress}>
<Text style={styles.buttonText}>{title}</Text>
</TouchableOpacity>
);
};
Try to pass the onPress handler to TouchableOpacity:
export const MediumButton = ({ title, onPress }) => {
return (
<TouchableOpacity style={styles.mediumButton} onPress={onPress}>
<Text style={styles.buttonText}>{title}</Text>
</TouchableOpacity>
);
};
I am new to React Native and I am having problems with the header in a FlatList.
The header re-renders as soon as I start to scroll, this creates a flickering effect on the images I have in the header.
I have been searching for an answer everywhere but I have not find a posible solution.
¿how could I configure it to stop re-rendering the header when scrolling the list?
....
const Item = ({ title }) => {
return (
<View style={styles.card}>
<Text style={styles.title}>{title}</Text>
</View>
);
};
const listHeader = () => {
const categoriesButtons = categories.map(cat => (
<CategoryButton
text={cat.name}
icon={cat.code}
key={cat.code}
onPress={() => {
//#Todo logic to filter promotions accordingly with the category pressed
}}
/>
));
return (
<View>
<View style={styles.filtersContainer}>
<ImageBackground
source={images.bgShape}
style={{ width: '100%', height: 140 }}
resizeMode="stretch"
>
<ScrollView horizontal showsHorizontalScrollIndicator={false}>
{categoriesButtons}
</ScrollView>
</ImageBackground>
</View>
<View style={styles.breadcrumbContainer}>
<Breadcrumbs navigation={navigation} stack={routes} />
</View>
<View style={styles.titleContainer}>
<Text style={sharedStyles.titleText} id="main-title">
¡{totalOfPromotions} promociones activas en MedellÃn!
</Text>
</View>
</View>
);
};
return (
<LinearGradient
colors={[Colors.BG_START, Colors.BG_END]}
style={styles.mainContainer}
>
{loading ? (
<ActivityIndicator size="large" color="#000000" />
) : (
<FlatList
data={promos}
renderItem={({ item }) => <Item title={item.title} />}
keyExtractor={(item, index) => index.toString()}
ListHeaderComponent={listHeader}
showsVerticalScrollIndicator={false}
onEndReached={showPromos}
onEndThreshold={0.7}
/>
)}
</LinearGradient>
);
};
listHeader() function is being called more than once because in Flatlist tag should be called as
<FlatList
data={promos}
renderItem={({ item }) => <Item title={item.title} />}
keyExtractor={(item, index) => index.toString()}
ListHeaderComponent={listHeader()}
showsVerticalScrollIndicator={false}
onEndReached={showPromos}
onEndThreshold={0.7}
/>
Use () while assigning ListHeaderComponent prop. By this way, function will be invoked only once.
Hope this help you. Enjoy coding!
From what I can see in the code you provided that you are defining the ListHeader component inside your other parent component which will redfine it on every render.
Moving it might outside the parent component might help.
You can fix your flickering issue by memoizing your ListHeaderComponent.
In your case just wrap your component with useMemo:
const listHeader = useMemo(() => {
...
})
I am new to react-native and recently implemented Redux in my app.
The App is for ordering food in Restaurants :)
This is a snippet of my Restaurant-Details Screen with cards, which are representing the different menus:
<View style={{alignItems: 'center'}}>
{menus.map((item) => {
return (
<Card style={{width: '95%'}}>
<TouchableOpacity onPress ={() => this.props.navigation.navigate('Products', { menuId: item.sk_id } ,{bestellung})}>
<CardItem cardBody style={{justifyContent: 'center', alignItems: 'center'}}>
<Text style={styles.textdes}> {item.typ} </Text>
</CardItem>
</TouchableOpacity>
</Card>
)
})}
As you can see the onPress Function navigates to the Product Screen and passes a menuId! (Product Screen shows all the food of a specific menu)
This is my Product.js:
class Products extends Component {
constructor(props) {
super(props);
this.state = {
loading: 1
};
}
componentDidMount = () => {
this.props.fetchProducts();
this.state.menuId;
}
addItemsToCart = (product) => {
this.props.addToCart(product);
}
render() {
const { products, navigation } = this.props
const {params} = this.props.navigation.state;
const menuId = params ? params.menuId : null;
return (
<View style={styles.container}>
<Header style={styles.header}>
<Left>
<Button transparent onPress={() =>
this.props.navigation.goBack()}>
<Icon style={{ fontSize: 35, color: '#FFFFFF'}}
active name="arrow-back" />
</Button>
</Left>
<Body>
<Title style={{ fontSize: 25}}>{menuId}</Title>
</Body>
<Right style={{position: 'absolute', right: 20}}>
<Cart navigation={navigation}/>
</Right>
</Header>
<StatusBar barStyle="light-content" backgroundColor="#383838"
translucent={false} />
<View style={styles.bodyProducts}>
<View>
<Text style={{color:
'#000', fontSize: 50}}>{menuId}</Text>
</View>
<FlatList
data={products}
renderItem={({item}) =>
<Product item={item} addItemsToCart=
{this.addItemsToCart} product={item}/>}
keyExtractor ={(item) => item.id}
/>
</View>
</View>
);
}
}
const mapStateToProps = (state) => ({
products: state.products.items
})
export default connect(mapStateToProps {addToCart,fetchProducts})(Products);
The Product.js fetches products from my product.component.js:
class Product extends Component {
addToCart = () => {
this.props.addItemsToCart(this.props.item)
}
render() {
const { product, menuId } = this.props;
return (
<Card>
<View style={styles.container}>
<Image source={product.picture} style={{width:400,height:150}}/>
<View style={styles.productDes}>
<CardItem cardBody style={{justifyContent: 'center',
alignItems: 'center'}}>
<Text style={styles.font}>{menuId}</Text>
</CardItem>
<Text style={{fontSize: 20}}>€{(product.cost).toFixed(2)}
</Text>
<Text style={{fontSize: 16}}>{product.description}</Text>
<TouchableOpacity onPress={this.addToCart} style=
{styles.addBtn}>
<Text style={styles.text}>Hinzufügen</Text>
</TouchableOpacity>
</View>
</View>
</Card>
);
}
}
export default Product;
The question is now: How can I pass the menuId from product.js to
product.component.js without naviagtion?
I hope I could make clear, what my problem is and looking forward to your solutions and help :)
Maybe you should better separate your React components - which are basically views - and your reducers / side-effect (data fetching...) components.
State is in Redux Store, Views only display what's in store.
If you don't want to pass menuId through navigation params, you can add it to your store state - basically you add a field in your reducer's state to save the currently selected menuId.
In your Restaurant-Details screen, when calling onPress, you dispatch an action with the selectedMenuId to your redux store.
Then in your product screen, you retrieve the selectedMenuId from your redux store.
To understand Redux architecture , I like this gif:
Since you're already using Redux, why not store the menu ID you're currently viewing in your store? When the user selects a menu item, dispatch an action to set the current menu ID. Then use a selector to to get the menu ID in the component you need it.
I hope I haven't misunderstood your issue.