I want to display model with the key value from array, but I can't do it, and I don't understand the problem.
this is react native in Expo and I have used react native elements
import React, {Component} from "react";
import { ScrollView, Text, Linking, View, Modal } from "react-native";
import { Card, Button } from "react-native-elements";
import PriceDetail from "./PriceDetail";
const images = [
{
key: 1,
name: "Nathan Anderson",
image: require("../images/1.jpg"),
url: "https://unsplash.com/photos/C9t94JC4_L8"
},
{
key: 2,
name: "Jamison McAndie",
image: require("../images/2.jpg"),
url: "https://unsplash.com/photos/waZEHLRP98s"
},
{
key: 3,
name: "Alberto Restifo",
image: require("../images/3.jpg"),
url: "https://unsplash.com/photos/cFplR9ZGnAk"
},
{
key: 4,
name: "John Towner",
image: require("../images/4.jpg"),
url: "https://unsplash.com/photos/89PFnHKg8HE"
}
];
class Home extends Component {
state = {
selectedItem : null,
mvisible : false
}
modalClosedHandler = () => {
this.setState({
mvisible: false,
selectedItem: null
});
};
productSelectedHandler = key => {
this.setState(prevState => {
return {
selectedItem: prevState.images.find(image => {
return image.key === key;
})
};
});
console.log(selectedItem);
};
showModal = (key) => {
this.setState({
mvisible: true,
selectedItem: key
});
}
render () {
return (
<View style={{ flex: 1 }}>
<Modal
visible={this.state.mvisible}
onRequestClose={this.modalClosedHandler}>
<View style={{flex : 1, alignItems: 'center', justifyContent: 'center'}}>
<Text>Hello this is modal{this.state.selectedItem}</Text>
<Button title="Close" onPress={this.modalClosedHandler}/>
</View>
</Modal>
<ScrollView contentContainerStyle={{ paddingVertical: 20 }}>
{images.map(({ name, image, url, key }) => (
<Card title={`Product ${key}`} image={image} key={key}>
<Text style={{ marginBottom: 10 }}>
Photo by {name}.
</Text>
<Button
backgroundColor="#03A9F4"
title="VIEW NOW"
onPress={(key)=>this.showModal(key)}
/>
</Card>
))}
</ScrollView>
</View>
);
}
}
export default Home;
I am new to react native.
You wont get key in the first parameter of button's onPress.
This is wrong:
<Button
backgroundColor="#03A9F4"
title="VIEW NOW"
onPress={(key)=>this.showModal(key)}
/>
You already have a key at higher level, so you should use this code:
<Button
backgroundColor="#03A9F4"
title="VIEW NOW"
onPress={()=>this.showModal(key)}
/>
I'm not sure to understand well but if you have a problem while calling this.showModal is because you have an extra "key".
Replace
<Button
backgroundColor="#03A9F4"
title="VIEW NOW"
onPress={(key)=>this.showModal(key)}
/>
By
<Button
backgroundColor="#03A9F4"
title="VIEW NOW"
onPress={()=>this.showModal(key)}
/>
Related
I am making a React Native application in which I have menus and submenus.
Menus and submenus structure:
let arr = [
{
name: 'Header 1',
routeName: 'Home',
child: [
{ name: 'Header 1 - Submenu 1', child: [], routeName: 'Home' },
{
name: 'Header 1 - Submenu 2',
child: [],
routeName: 'NotificationScreen',
},
{ name: 'Header 1 - Submenu 3', child: [], routeName: 'Home' },
],
},
{
name: 'Header 2',
routeName: 'NotificationScreen',
child: [
{
name: 'Header 2 - Submenu 1',
child: [],
routeName: 'NotificationScreen',
},
{ name: 'Header 2 - Submenu 2', child: [], routeName: 'Home' },
{
name: 'Header 2 - Submenu 3',
child: [],
routeName: 'NotificationScreen',
},
],
},
];
Render of Menu's and Submenu's:
<TouchableOpacity style={styles.item} onPress={onPress} activeOpacity={1}>
<View style={styles.row}>
<Text style={{ paddingRight: 20 }}>{name}</Text>
{child.length ? <Text>{open ? 'close' : 'open'}</Text> : null}
</View>
{open &&
child.map((x: any, i: any) => {
if (x.child.length) {
return (
<Item
key={x}
active={childActive}
i={i}
setActive={setChildActive}
child={x.child}
/>
);
}
return (
<TouchableOpacity
key={x}
style={styles.subItem}
onPress={() => {
handleRouteChange(x.routeName);
}}>
<Text>
{name} - submenu - {i + 1}
</Text>
</TouchableOpacity>
);
})}
</TouchableOpacity>
Working example: https://snack.expo.dev/#manirajmurugan/custom-header-title-component
Here I am in the need to make a breadcrumb like structure on the screen page as per the navigation done by user through menu and submenus.
Current Scenario:
-> If user clicks on Header 1 and then select submenu Header 1 - Submenu 1, then the user will be redirected to Home Screen.
Expected Scenario:
-> Requirement here is that I am in the need to display a breadcrumb for this screen like,
Header 1 > Header 1 - Submenu 1
On click of the Header 1 in this breadcrumb, user will be redirected to the respective routeName given in the object.
Kindly help me to generate breadcrumb for the navigation done in menu for the respective screen's.
Thanks in advance.
Edit:
In real app menu and submenu will be like,
Here if user click's on Stored Data configuration under Unit Data Management,
then the expected breadcrumb result would be,
Create a piece of state to hold the crumbs. It will be an array that will hold three values: the screen you are currently on, the section title press, and the section item pressed. Handling the last two will be done by the SectionList and to keep track of the current screen you can add a screenListener to the StackNavigator that will listen for all navigation changes on all screens.
import React, { useState, createContext } from 'react';
import { View, StyleSheet, useWindowDimensions } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import Header from './screens/Header';
import HomeScreen from './screens/Home';
import SettingsScreen from './screens/Settings';
const Stack = createNativeStackNavigator();
import { ICON_SIZE, headerHeight } from './Constants';
const initialScreen = 'Settings';
export default function App() {
// this will have at most 3 items:
// the current screen
// the section header
// the section.data[index]
const [crumbNavigation, setCrumbNavigation] = useState([initialScreen]);
const { width, height } = useWindowDimensions();
const screenOptions = {
// pass crumbNavigation and its setter to header
headerTitle: (props) => (
<Header
{...props}
width={width}
height={headerHeight}
crumbNavigation={crumbNavigation}
setCrumbNavigation={setCrumbNavigation}
/>
),
headerStyle: {
height: headerHeight,
width: width,
},
// since there will be a bread crumb navigator
// theres no need for back button
headerBackVisible: false,
headerLeft: () => null,
};
return (
<NavigationContainer>
<Stack.Navigator
initialRouteName={initialScreen}
screenOptions={screenOptions}
screenListeners={{
state: (nav) => {
console.log(nav.type);
const routes = nav.data.state.routes;
const lastRoute = routes[routes.length - 1]?.name;
if (lastRoute)
setCrumbNavigation((prev) => {
prev[0] = lastRoute;
return prev;
});
},
}}>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Settings" component={SettingsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
Now that current screen is handled, you can move on to deal with getting the SectionTitle, which the SectionHeader and MenuItem components handles. If a section data array's length is zero, pressing it will trigger the parent component's onItemPress with the section as an argument, if not, then pressing it will reveal its children
import { useState } from 'react';
import { TouchableOpacity, Text, View, StyleSheet } from 'react-native';
import TouchableMaterialIcon from './TouchableMaterialIcon';
import { ICON_SIZE } from '../Constants';
export default function SectionHeader({ section, toggleSectionMenu, onPress }) {
return (
<TouchableOpacity
style={[
styles.headerContainer,
section.isOpen && styles.highlightedTitleContainer,
]}
onPress={()=>{
if(section.data.length > 0)
toggleSectionMenu(section.id)
else
onPress(section.title)
}}
>
<View style={{ flex: 1 }}>
<Text
styles={[
styles.titleText,
section.isOpen && styles.highlightedTitleText,
]}>
{section.title}
</Text>
</View>
{section.data.length > 0 && (
<TouchableMaterialIcon
name={section.isOpen ? 'keyboard-arrow-up' : 'keyboard-arrow-down'}
size={ICON_SIZE * 0.6}
onPress={() => toggleSectionMenu(section.id)}
/>
)}
</TouchableOpacity>
);
}
const styles = StyleSheet.create({
headerContainer: {
width: '100%',
flexDirection: 'row',
// justifyContent:'center',
alignItems: 'center',
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: '#eee',
paddingVertical: 5,
},
titleText: {
fontSize: 16,
},
highlightedTitleText: {
fontWeight: 'bold',
},
highlightedTitleContainer: {
backgroundColor: '#edf',
},
});
The SectionItem is pretty straightforward. When visible, its a button that when pressed will trigger its grandparent's onItemPressed with both the section title and the item's title
import { View, StyleSheet, Text, TouchableOpacity } from 'react-native';
export default function MenuItem({ section, index, item, onPress }) {
const isLastItem = index == section.data.length - 1;
if (section.isOpen)
return (
<TouchableOpacity
style={[styles.itemContainer, isLastItem && styles.lastItem]}
onPress={() => onPress(section.title, item)}>
<Text>{item}</Text>
</TouchableOpacity>
);
}
const styles = StyleSheet.create({
itemContainer: {
marginRight: 5,
padding: 5,
},
lastItem: {
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: '#eee',
},
});
ButtonWithMenu contains a button that when pressed, renders the SectionList directly beneath it. It accepts an onItemPress prop which will be passed on into the SectionList:
import React, { useState } from 'react';
import {
Text,
View,
StyleSheet,
SectionList,
useWindowDimensions,
Modal,
TouchableWithoutFeedback,
} from 'react-native';
import TouchableMaterialIcon from './TouchableMaterialIcon';
import MenuItem from './MenuItem';
import SectionHeader from './SectionHeader';
import useLayout from '../hooks/useLayout';
export default function DropDownButtonMenu({
name,
size,
color,
onItemPress,
buttonStyle,
containerStyle,
menu,
onMenuAction,
}) {
const [openMenu, setOpenMenu] = useState(false);
const [buttonLayout, onButtonLayout] = useLayout();
// add additional state to menu
const [menuItems, setMenuItems] = useState(
menu?.map((section, i) => {
section.isOpened = false;
section.id = `section-item-${i}`;
return section;
}) || []
);
const { width, height } = useWindowDimensions();
const handleButtonPress = () => {
setOpenMenu((prev) => !prev);
};
const toggleSectionMenu = (sectionId) => {
// deep clone menuItems
const newData = JSON.parse(JSON.stringify(menuItems));
const index = newData.findIndex((section) => section.id == sectionId);
if (index < 0) {
console.log('Section id not found');
return;
}
newData[index].isOpen = !newData[index].isOpen;
setMenuItems(newData);
};
const handleItemPress = (sectionTitle,itemTitle)=>{
onItemPress(sectionTitle,itemTitle)
setOpenMenu(false)
}
return (
<View style={[containerStyle, { zIndex: 1 }]}>
<TouchableMaterialIcon
style={buttonStyle}
name={name}
size={size}
color={color}
onLayout={onButtonLayout}
onPress={handleButtonPress}
disabled={menuItems.length < 1}
/>
{/*
wrap modal in touchable so that pressing outside modal will close it
tried to use absolute positioned view but no matter the zIndex but
the screen contents would always appear on top
*/}
<TouchableWithoutFeedback onPress={() => setOpenMenu(false)}>
<Modal transparent visible={openMenu}>
<View
style={[
styles.menu,
{
top: buttonLayout.y + buttonLayout.height,
left: buttonLayout.left,
minWidth: 150,
maxHeight: height * 0.6,
},
]}>
<SectionList
sections={menuItems}
renderItem={(props) => <MenuItem {...props} onPress={handleItemPress} />}
renderSectionHeader={(props) => (
<SectionHeader
{...props}
toggleSectionMenu={toggleSectionMenu}
onPress={handleItemPress}
/>
)}
/>
</View>
</Modal>
</TouchableWithoutFeedback>
</View>
);
}
const styles = StyleSheet.create({
menu: {
position: 'absolute',
backgroundColor: 'white',
padding: 10,
zIndex: 10,
},
});
Now in the Header component the only thing that is left to do is create a list for the SectionList to render and then update the crumb state with onItemPress:
import { useContext } from 'react';
import { View, StyleSheet, Text, useWindowDimensions } from 'react-native';
import ButtonWithMenu from '../components/ButtonWithMenu';
import { LinearGradient } from 'expo-linear-gradient';
import { ICON_SIZE } from '../Constants';
export default function Header({width,height,crumbNavigation,setCrumbNavigation}) {
return (
<View style={{flex:1,height, width}}>
<View style={styles.navbar}>
<ButtonWithMenu
containerStyle={[styles.navbarButton,{marginLeft:ICON_SIZE}]}
name="home"
size={ICON_SIZE}
color="black"
/>
<ButtonWithMenu
containerStyle={styles.navbarButton}
name="close"
size={ICON_SIZE}
color="black"
onItemPress={(sectionTitle, itemTitle) =>
setCrumbNavigation((prev) => {
const newCrumbNav = [prev[0]];
if (sectionTitle) newCrumbNav.push(sectionTitle);
if (itemTitle) newCrumbNav.push(itemTitle);
return newCrumbNav;
})
}
menu={[
{ title: 'Disconnect from Unit', data: [] },
{ title: 'Monitor Unit', data: ['This Thing', 'That Thing'] },
{ title: 'Unit Settings', data: ['This Thing', 'That Thing'] },
{
title: 'Unit Data Management',
data: ['Stored Data Configuration', 'Stored Data Snapshot'],
},
{ title: 'Information', data: ['This Thing', 'That Thing'] },
{ title: 'Help', data: ['This Thing', 'That Thing'] },
]}
/>
</View>
<LinearGradient
colors={['#902337', '#21458d']}
style={styles.colorBar}
start={{ x: 1, y: 1 }}
locations={[0.5, 0.8]}
/>
<View style={styles.crumbs}>
<Text style={styles.crumbScreen} numberOfLines={1}>
{crumbNavigation[0]} {crumbNavigation.length > 1 && ' > '}
<Text style={styles.crumbTitle}>
{crumbNavigation.slice(1).join(' : ')}
</Text>
</Text>
</View>
</View>
);
}
const styles = StyleSheet.create({
navbar: {
flexDirection: 'row',
},
colorBar: {
height: 5,
width: '100%',
},
navbarButton: {
// marginHorizontal: 10,
paddingHorizontal: ICON_SIZE * 0.25,
},
crumbs: {
overflow:'hidden',
flexWrap:'none'
},
crumbScreen: {
fontWeight: 'bold',
},
crumbTitle: {
color: '#21458d',
fontWeight: '400',
},
});
Demo
For readability, I started by moving all of the components into their own separate files. I also used a FlatList instead of a ScrollView for the recursive SubMenuItem rendering.
I first broke the breadcrumb down into three components. The first, MenuItem, which would always be visible in the Header. When pressed, it would reveals a dropdown menu, which would reveal the SubMenu, MenuItem's children. Pressing SubMenu would reveal its children, if it had any, or would trigger navigation, if a routeName was provided. Demo
how do i do a loop for 10 cards? Cant find any solution anywhere.
I need it to output cards with some info which ill specify later. I am new to React Native and got no idea what Im doing. It worked when ive written return into the loop, but it returned just one card (better than none I guess).
import * as React from 'react';
import { StyleSheet, ScrollView, View, Text, Image } from 'react-native';
import { Card, CardTitle, CardContent, CardAction, CardButton, CardImage } from 'react-native-material-cards'
export default function Primary({ navigation })
{
return(
<ScrollView style=
{{
flex: 1,
}}>
<View>
<Text style={{ fontSize: 26, fontWeight: 'bold' }}>
Hlavni
</Text>
<Text>Ahojda</Text>
</View>
<Text>Swag</Text>
<KartyLoop/>
</ScrollView>
);
}
const KartyLoop = () => {
var swag[];
for (i=0; i<5; i++)
{
<View>
<Card style={{borderRadius: 25}}>
<CardTitle
title="This is a title"
subtitle="This is subtitle"
/>
<CardContent text="Your device will reboot in few seconds once successful, be patient meanwhile" />
<CardAction
separator={true}
inColumn={false}>
<CardButton
onPress={() => {}}
title="Push"
color="blue"
/>
<CardButton
onPress={() => {}}
title="Later"
color="blue"
/>
</CardAction>
</Card>
</View>
}
return();
}
When rendering a list of components in react native, you need to put them in some sort of list.
Try wrapping the code inside your <View> with <Flatlist>
Docs: https://reactnative.dev/docs/flatlist
Full example from docs:
import React from 'react';
import { SafeAreaView, View, FlatList, StyleSheet, Text, StatusBar } from 'react-native';
const DATA = [
{
id: 'bd7acbea-c1b1-46c2-aed5-3ad53abb28ba',
title: 'First Item',
},
{
id: '3ac68afc-c605-48d3-a4f8-fbd91aa97f63',
title: 'Second Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29d72',
title: 'Third Item',
},
];
const Item = ({ title }) => (
<View style={styles.item}>
<Text style={styles.title}>{title}</Text>
</View>
);
const App = () => {
const renderItem = ({ item }) => (
<Item title={item.title} />
);
return (
<SafeAreaView style={styles.container}>
<FlatList
data={DATA}
renderItem={renderItem}
keyExtractor={item => item.id}
/>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: StatusBar.currentHeight || 0,
},
item: {
backgroundColor: '#f9c2ff',
padding: 20,
marginVertical: 8,
marginHorizontal: 16,
},
title: {
fontSize: 32,
},
});
export default App;
thanks for reading my question! I am struggling with this problem since a few days now: My Flatlist component re-renders all items in the list every time I make a change to the underlying data.
Situation:
I have a FlatList component rendering Items which contain a TouchableOpacity object to toggle Favorite status on this item.
If this button is pressed, I expect only this specific item to change/re-render in my FlatList instead of all items. It feels like as soon as I update state by calling setListData, it re-renders everything.
I have encountered this issue in a more complex setup but was able to drill it down to this core problem. Or is this actually the expected behavior?
Code:
import React, { useState } from "react";
import {
View,
Text,
StyleSheet,
FlatList,
TouchableOpacity,
} from "react-native";
const PlanerScreen = () => {
const [listData, setListData] = useState([
{ id: "1", name: "Banana", isFav: true },
{ id: "2", name: "Apple", isFav: false },
]);
const Item = ({ item, onPressHandler }) => {
console.log(item.name, " rendered");
const color = item.isFav ? "red" : "green";
return (
<View
style={{
flexDirection: "row",
width: "100%",
margin: 10,
}}
>
<Text>{item.name}</Text>
<TouchableOpacity
style={{ width: 100, height: 50, backgroundColor: color }}
onPress={onPressHandler}
/>
</View>
);
};
const favHandler = (id) => {
setListData(
listData.map((item) =>
item.id === id ? { ...item, isFav: !item.isFav } : item
)
);
};
console.log("FlatList rendered");
return (
<View style={{ flex: 1 }}>
<StatusBar style={selectedTheme === "light" ? "dark" : "light"} />
<FlatList
data={listData}
renderItem={({ item }) => (
<Item item={item} onPressHandler={() => favHandler(item.id)} />
)}
keyExtractor={(item) => item.id}
/>
</View>
);
};
export default PlanerScreen;
Console Output on clicking the Favorite Toggle Button:
FlatList rendered
Banana rendered
Apple rendered
FlatList rendered
Banana rendered
Apple rendered
FlatList rendered
Banana rendered
Apple rendered
You can use React.memo which is an alternative to shouldComponentUpdate for functional components.
It tells React when to re-render the component based on prev and next props.
import React, { useState, useCallback } from "react";
import {
View,
Text,
StyleSheet,
FlatList,
TouchableOpacity,
} from "react-native";
const styles = StyleSheet.create({
container: {
flex: 1,
}
})
const keyExtractor = (item) => item.id;
const Item = React.memo(({ item, onPressHandler }) => {
console.log(item.name, " rendered");
const color = item.isFav ? "red" : "green";
return (
<View
style={{
flexDirection: "row",
width: "100%",
margin: 10,
}}
>
<Text>{item.name}</Text>
<TouchableOpacity
style={{ width: 100, height: 50, backgroundColor: color }}
onPress={() => onPressHandler(item.id)}
/>
</View>
);
}, (prevProps, nextProps) => {
if (prevProps.item.isFav === nextProps.item.isFav) return true;
return false;
});
const PlanerScreen = () => {
const [listData, setListData] = useState([
{ id: "1", name: "Banana", isFav: true },
{ id: "2", name: "Apple", isFav: false },
]);
const favHandler = useCallback((id) => {
setListData(prevState => {
return prevState.map((item) =>
item.id === id ? { ...item, isFav: !item.isFav } : item
)
}
);
}, []);
console.log("### FlatList rendered #####");
return (
<View style={styles.container}>
<FlatList
data={listData}
renderItem={({ item }) => <Item item={item} onPressHandler={favHandler} />}
keyExtractor={keyExtractor}
/>
</View>
);
};
export default PlanerScreen;
I already have data in my realtime database. I can also fetch it successfully. Now what I need to do is output it in a tinder-like card view. I am currently outputting data using a 'Demo' file which exports a hard-coded array that contains sample information of some users. Using the demo array, the card output is working. But when I fetch data from firebase and store it in an array named items[], nothing is displayed on my output screen. My code is as follows:
Home Screen
import React from 'react';
import { View, ImageBackground } from 'react-native';
import CardStack, { Card } from 'react-native-card-stack-swiper';
import City from '../components/City';
import Filters from '../components/Filters';
import CardItem from '../components/CardItem';
import styles from '../assets/styles';
import Demo from '../assets/demo';;
import {db} from '../config/config';
class Home extends React.Component {
constructor (props) {
super(props);
this.state = ({
items: []
});
}
componentWillMount() {
var items = [];
db.ref('cards').once('value', (snap) => {
snap.forEach((child) => {
let item = child.val();
item.id = child.key;
items.push({
pet_name: child.val().pet_name,
pet_gender: child.val().pet_gender,
pet_age: child.val().pet_age,
pet_breed: child.val().pet_breed,
photoUrl: child.val().photoUrl,
});
});
//console.log(items)
this.setState({ items: items });
console.log(items);
});
}
render() {
return (
<ImageBackground
source={require('../assets/images/bg.png')}
style={styles.bg}
>
<View style={styles.containerHome}>
<View style={styles.top}>
<City />
<Filters />
</View>
<CardStack
loop={true}
verticalSwipe={false}
renderNoMoreCards={() => null}
ref={swiper => {
this.swiper = swiper
}}
>
{Demo.map((item, index) => (
<Card key={index}>
<CardItem
image={item.image}
name={item.name}
description={item.description}
actions
onPressLeft={() => this.swiper.swipeLeft()}
onPressRight={() => this.swiper.swipeRight()}
/>
</Card>
))}
</CardStack>
</View>
</ImageBackground>
)
}
}
export default Home;
Demo Data
module.exports = [
{
id: 3,
name: 'whatever',
status: 'Online',
match: '78',
description:
'Full-time Traveller. Globe Trotter. Occasional Photographer. Part time Singer/Dancer.',
message:
'I will go back to Gotham and I will fight men Iike this but I will not become an executioner.',
image: require('./images/01.jpg')
},
{
id: 2,
name: 'Clementine Bauch',
match: '93',
description:
'Full-time Traveller. Globe Trotter. Occasional Photographer. Part time Singer/Dancer.',
status: 'Offline',
message: "Someone like you. Someone who'll rattle the cages.",
image: require('./images/02.jpg')
}
];
fetching data:
import {db} from '../config/config';
var items = [];
db.ref('cards').once('value', (snap) => {
snap.forEach((child) => {
let item = child.val();
item.id = child.key;
items.push({
pet_name: child.val().pet_name,
pet_gender: child.val().pet_gender,
pet_age: child.val().pet_age,
pet_breed: child.val().pet_breed,
photoUrl: child.val().photoUrl,
});
});
//console.log(items)
for (var i in items){
console.log(items[i])
}
});
export var items;
I want to use my items[] array instead of the Demo hard-coded array. How do I do this?
The output of items[] array:
Array [
Object {
"pet_age": "11",
"pet_breed": "golden retriever",
"pet_gender": "male",
"pet_name": "dawn",
"photoUrl": "picture",
},
Object {
"pet_age": "7",
"pet_breed": "German",
"pet_gender": "Male",
"pet_name": "Rambo",
"photoUrl": "https://firebasestorage.googleapis.com/v0/b/woofmatix-50f11.appspot.com/o/pFkdnwKltNVAhC6IQMeSapN0dOp2?alt=media&token=36087dae-f50d-4f1d-9bf6-572fdaac8481",
},
]
CardItem component:
import React from 'react';
import styles from '../assets/styles';
import { Text, View, Image, Dimensions, TouchableOpacity } from 'react-native';
import Icon from './Icon';
const CardItem = ({
actions,
description,
image,
matches,
name,
pet_name,
pet_gender,
pet_age,
onPressLeft,
onPressRight,
status,
variant
}) => {
// Custom styling
const fullWidth = Dimensions.get('window').width;
const imageStyle = [
{
borderRadius: 8,
width: variant ? fullWidth / 2 - 30 : fullWidth - 80,
height: variant ? 170 : 350,
margin: variant ? 0 : 20
}
];
const nameStyle = [
{
paddingTop: variant ? 10 : 15,
paddingBottom: variant ? 5 : 7,
color: '#363636',
fontSize: variant ? 15 : 30
}
];
return (
<View style={styles.containerCardItem}>
{/* IMAGE */}
<Image source={image} style={imageStyle} />
{/* MATCHES */}
{matches && (
<View style={styles.matchesCardItem}>
<Text style={styles.matchesTextCardItem}>
<Icon name="heart" /> {matches}% Match!
</Text>
</View>
)}
{/* NAME */}
<Text style={nameStyle}>{name}</Text>
{/* DESCRIPTION */}
{description && (
<Text style={styles.descriptionCardItem}>{description}</Text>
)}
{/* STATUS */}
{status && (
<View style={styles.status}>
<View style={status === 'Online' ? styles.online : styles.offline} />
<Text style={styles.statusText}>{pet_age}</Text>
</View>
)}
{/* ACTIONS */}
{actions && (
<View style={styles.actionsCardItem}>
<View style={styles.buttonContainer}>
<TouchableOpacity style={[styles.button, styles.red]} onPress={() => {
this.swiper.swipeLeft();
}}>
<Image source={require('../assets/red.png')} resizeMode={'contain'} style={{ height: 62, width: 62 }} />
</TouchableOpacity>
<TouchableOpacity style={[styles.button, styles.orange]} onPress={() => {
this.swiper.goBackFromLeft();
}}>
<Image source={require('../assets/back.png')} resizeMode={'contain'} style={{ height: 32, width: 32, borderRadius: 5 }} />
</TouchableOpacity>
<TouchableOpacity style={[styles.button, styles.green]} onPress={() => {
this.swiper.swipeRight();
}}>
<Image source={require('../assets/green.png')} resizeMode={'contain'} style={{ height: 62, width: 62 }} />
</TouchableOpacity>
</View>
</View>
)}
</View>
);
};
export default CardItem;
Consider the following component where the user selects an option from a list:
import React, { Component } from "react";
import PropTypes from "prop-types";
import {
View,
Text,
StyleSheet,
Platform,
FlatList,
TouchableNativeFeedback,
TouchableOpacity,
PLatform
} from "react-native";
import Icon from "react-native-vector-icons/Ionicons";
import { colors, metrics } from "../../themes";
const Touchable =
Platform.OS === "android" ? TouchableNativeFeedback : TouchableOpacity;
class MenuSelector extends Component<{
onSelect: () => any,
config: object,
selected: string
}> {
state = {
listOpened: false
};
handlePress = id => {
this.props.onPress(id);
};
handleSelect = id => {
if (this.props.onSelect) this.props.onSelect(id);
this.setState({
listOpened: false
});
};
handleMenu = () => {
this.setState({
listOpened: !this.state.listOpened
});
};
render = () => {
let title = "";
if (this.props.config) {
title = this.props.config[0].title;
if (this.props.selected) {
let found = this.props.config.find(item => {
return item.id === this.props.selected;
});
if (found) title = found.title;
}
}
let top = (
<View style={styles.header}>
<Text style={styles.text}>{title}</Text>
<Touchable>
<Text style={styles.text}>
<Icon
name={"ios-menu"}
size={20}
onPress={this.handleMenu}
/>
</Text>
</Touchable>
</View>
);
let list = null;
if (this.state.listOpened === true) {
list = (
<FlatList
data={this.props.config}
renderItem={({ item }) => (
<Touchable onPress={this.handleSelect}>
<Text style={[styles.text, styles.listItem]}>{item.title}</Text>
</Touchable>
)}
/>
);
}
return (
<View style={styles.container}>
{top}
{list}
</View>
);
};
}
export default MenuSelector;
const styles = StyleSheet.create({
container: {
flex: -1,
flexDirection: "column"
},
header: {
flex: -1,
flexDirection: "row",
justifyContent: "space-between",
padding: 10,
backgroundColor: "blue"
},
text: {
fontSize: 16,
color: "white",
textAlign: "center",
fontWeight: "bold"
},
listItem: {
padding: 10,
backgroundColor: "blue"
}
});
The component is used in the following context:
let config = [
{
title="Option 1",
id="option1"
},
{
title="Option 2",
id="option2"
},
{
title="Option 3",
id="option3"
},
{
title="Option 4",
id="option4"
},
];
return (
<View style={styles.container}>
<MenuSelector
config={config.options}
selected={this.state.mode}
onPress={this.handleButtonListPress}
/>
<FlatList
data={this.props.data}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item }) => (
<Text style={styles.text}>{item.name}</Text>
)}
/>
</View>
);
As it is, the <MenuSelector> component appears "at once" on screen. I need to add an sliding effect to <MenuSelector>, "pushing down" the data FlatList when appearing on screen...
On closing, same behaviour, but animating from down to up.
How can I add such animation behaviour to my MenuSelector component ?