I came across the following React Native code online:
import React, { useState } from 'react';
import { StyleSheet, Text, View, FlatList } from 'react-native';
export default function App() {
const [people, setPeople] = useState([
{ name: 'shaun', id: '1' },
{ name: 'yoshi', id: '2' },
{ name: 'mario', id: '3' },
{ name: 'luigi', id: '4' },
{ name: 'peach', id: '5' },
{ name: 'toad', id: '6' },
{ name: 'bowser', id: '7' },
]);
return (
<View style={styles.container}>
<FlatList
numColumns={2}
keyExtractor={(item) => item.id}
data={people}
renderItem={({item}) => (
<Text style={styles.item}>{item.name}</Text>
)}
/>
</View>
);
}
I do not quite following the snippet related to the renderItem property. why are we using the destructuring syntax {item} . Also this is an object destructuring syntax (using curly braces), but in object destructuring we use keys as variable names. So I am afraid I am completley at a loss to understand the snippet below.
<FlatList
numColumns={2}
keyExtractor={(item) => item.id}
data={people}
renderItem={({item}) => (
<Text style={styles.item}>{item.name}</Text>
)}
Any guidance would be appreciated.
this...
renderItem={({item}) => (
<Text style={styles.item}>{item.name}</Text>
)}
actually mean....
renderItem={(props) => (
<Text style={styles.item}>{props.item.name}</Text>
)}
This technique is called object destruction
check out for more info - https://medium.com/podiihq/destructuring-objects-in-javascript-4de5a3b0e4cb
Related
I'm working with a custom dropdown menu in React Native and am having a problem changing the text when trying to reuse the dropdown in other components.
DropdownMenu.js
export const DropdownMenu = ({dropdownMenuItems}) => {
const [isActive, setIsActive] = React.useState(false);
const onPress = () => {
setIsActive(!isActive);
};
return (
<TouchableOpacity
activeOpacity={1}
onPress={() => setIsActive(false)}
style={{flex: 1}}>
<View>
<TouchableOpacity style={styles.img} onPress={onPress}>
<Image style={styles.imgimg} source={require('./icon.png')} />
</TouchableOpacity>
<Animated.View
style={{
...styles.menu,
opacity: fadeAnim,
transform: [{translateY: translateAnim}],
}}
pointerEvents={isActive ? 'auto' : 'none'}>
<FlatList
data={dropdownMenuItems}
renderItem={({item, index}) => (
<OpenURLButton
url={item.href}
label={item.name}
style={
index === 0
? {borderTopLeftRadius: 8, borderTopRightRadius: 8}
: index === dropdownMenuItems.length - 1
? {borderBottomLeftRadius: 8, borderBottomRightRadius: 8}
: {}
}
/>
)}
/>
</Animated.View>
</View>
</TouchableOpacity>
);
};
In the CompactMenu component, I import my <DropdownMenu /> and set my initial values for my menu:
CompactMenu.js
import React from 'react';
import {SafeAreaView} from 'react-native';
import {DropdownMenu} from './DropdownMenu';
const CompactMenu = () => {
const backgroundStyle = {
backgroundColor: '#fff',
flex: 1,
display: 'flex',
};
const dropdownMenuItems = [
{name: 'Messages', href: '/messages'},
{name: 'Trips', href: '/trips'},
{name: 'Saved', href: '/saved'},
];
return (
<SafeAreaView style={backgroundStyle}>
<DropdownMenu dropdownMenuItems={dropdownMenuItems} />
</SafeAreaView>
);
};
export default CompactMenu;
After importing <CompactMenu /> into another component, I try to change the name & the href in object:
example import:
<CompactMenu dropdownMenuItems={[{name: "changed name", href: "/somePath"}]} />
However, the same strings from CompactMenu.js are displayed in the dropdown.
Being new to RN, I'm not sure of two things here.
1.) Why do the text value for "name" not change when used in a different component?
2.) Shouldn't navigation to another screen use { navigation } instead of href:? I've tried adding onPress={() => navigation.navigate('SomeScreen') in the href but I get an error.
I'm not sure what the correct solution to this is. Any help would be appreciated.
You are not using the props dropdownMenuItems that you are passing to CompactMenu. Instead, you reuse the same menu items for the DropdownMenu component everytime you create a CompactMenu.
You need to use the props that you are passing. I have kept the static items as a default value. If you would like to have these items as well and to add additional items via props, then have a look at the second solution.
Notice that I have integrated some small changes to the rest of the code as well.
const defaultItems = [
{name: 'Messages', href: '/messages'},
{name: 'Trips', href: '/trips'},
{name: 'Saved', href: '/saved'},
];
const CompactMenu = ({dropdownMenuItems = defaultItems}) => {
return (
<SafeAreaView style={styles.backgroundStyle}>
<DropdownMenu dropdownMenuItems={dropdownMenuItems} />
</SafeAreaView>
);
};
const styles = StyleSheet.create({
backgroundStyle: {
backgroundColor: '#fff',
flex: 1,
}
});
Now using the CompactMenu component can receive dropdownMenuItems and will pas them to the DropdownMenu component.
<CompactMenu dropdownMenuItems={[{name: "changed name", href: "/somePath"}]} />
If you want to keep default items and add additional items via props, we could achieve this by merging the provided props with our default items.
const defaultItems = [
{name: 'Messages', href: '/messages'},
{name: 'Trips', href: '/trips'},
{name: 'Saved', href: '/saved'},
];
const CompactMenu = ({dropdownMenuItems}) => {
return (
<SafeAreaView style={styles.backgroundStyle}>
<DropdownMenu dropdownMenuItems={[...defaultItems, ...dropdownMenuItems]} />
</SafeAreaView>
);
};
Your second questions addresses the react-navigation framework for react-native. This is a very broad topic and I am assuming from your question that you don't know how this works yet, since you have not setup the necessary structure for using it. I encourage you to go through the documentation first.
In summary, you will need to define a navigator, e.g. a stack-navigator and add a name reference for each of your screens to the dropdown menu. To keep things short, here is a minimal example on how this could work.
const MenuScreen1 = (props) {
return (...)
}
const MenuScreen2 = (props) {
return (...)
}
const CompactMenu = ({dropdownMenuItems, navigation}) => {
return (
<SafeAreaView style={styles.backgroundStyle}>
<DropdownMenu dropdownMenuItems={dropdownMenuItems} navigation={navigation} />
</SafeAreaView>
);
};
const dropdownMenuItems = [
{name: 'Menu Item 1', screen: 'Item1'},
{name: 'Menu Item 2', screen: 'Item2'},
]
const Home = ({navigation}) {
return <CompactMenu navigation={navigation} dropdownMenuItems={dropdownMenuItems} />
}
const Stack = createNativeStackNavigator();
function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Item1" component={MenuScreen1} />
<Stack.Screen name="Item2" component={MenuScreen2} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default App;
Notice that I have created two screens whose names, which I have defined in the stack navigator, are provided to the CompactMenu. Notice as well that the navigation framework will pas the navigation object only to components that are defined as a screen, thus I have decided to pass the navigation object to the CompactMenu and the DropdownMenu component. You can use the useNavigation hook if you prefer this method.
Now, in your DropdownMenu you can navigate on click to the defined screens.
export const DropdownMenu = ({dropdownMenuItems, navigation}) => {
const [isActive, setIsActive] = React.useState(false);
const onPress = () => {
setIsActive(!isActive);
};
return (
<TouchableOpacity
activeOpacity={1}
onPress={() => setIsActive(false)}
style={{flex: 1}}>
<View>
<TouchableOpacity style={styles.img} onPress={onPress}>
<Image style={styles.imgimg} source={require('./icon.png')} />
</TouchableOpacity>
<Animated.View
style={{
...styles.menu,
opacity: fadeAnim,
transform: [{translateY: translateAnim}],
}}
pointerEvents={isActive ? 'auto' : 'none'}>
<FlatList
data={dropdownMenuItems}
renderItem={({item, index}) => (
<Button
onPress={() => navigation.navigate(item.screen)}
title={item.name}
style={
index === 0
? {borderTopLeftRadius: 8, borderTopRightRadius: 8}
: index === dropdownMenuItems.length - 1
? {borderBottomLeftRadius: 8, borderBottomRightRadius: 8}
: {}
}
/>
)}
/>
</Animated.View>
</View>
</TouchableOpacity>
);
};
I tried to map my data using array.prototype.map() but the data merges into one line, and I tried using flatlist as alternative but it doesn't show anything..... Can someone help me?
const array = [
{name: 'Almer', id: 1},
{name: 'Donat', id: 2},
{name: 'Ardi', id: 3},
];
above is the data
{array.map((e) => {
return (
<View>
<Text>{e.name}</Text>
</View>
);
})}
Any Help or reference will save my day! Thank You
Tried with no error:
Code:
import React from "react";
import { Text, SafeAreaView, View, FlatList } from "react-native";
const array = [
{ name: 'Almer', id: 1 },
{ name: 'Donat', id: 2 },
{ name: 'Ardi', id: 3 },
];
const Test = () => {
return (
<SafeAreaView>
<Text>--- array with map ---</Text>
{array.map((e) => {
return (
<View>
<Text>{e.name}</Text>
</View>
);
})}
<Text>--- FlatList --- </Text>
<FlatList
data={array}
renderItem={({ item }) => (
<View>
<Text>{item.name}</Text>
</View>
)}
keyExtractor={item => item.id.toString()}
/>
</SafeAreaView>
)
};
export default Test;
And snack for furthur changes: Expo example
I have a list of category from calling external API and I am displaying it in a flatlist like this in horizontal scroll
but it is not changing state onPress , I want it should change color when user click on particular tab/button
my API json format data is like this
const data = [
{
id: '1',
title: 'General'
},
{
id: '2',
title: 'Student-Visa'
},
{
id: '3',
title: 'Study'
},
{
id: '4',
title: 'Festival'
},
{
id: '5',
title: 'NorthIndian-Food'
},
]
and I am using it as
const renderItem = ({ item, index }) => (
<View key={index} style={selectedCategory === item.title ? styles.activeCategory : styles.categoryContainer}>
<TouchableOpacity
activeOpacity={0.6}
underlayColor={COLORS.white}
onPress={()=>handleCategory(item.title)}
>
<Text>{item.title}</Text>
</TouchableOpacity>
</View>
);
return (
<List
data={data}
renderItem={renderItem}
horizontal={true}
style={styles.container}
contentContainerStyle={styles.contentContainer}
showsHorizontalScrollIndicator={false}
keyExtractor={item => item.id.toString()}
/>
)
const handleSelectedCategory = (title) => {
setSelectedCategory(title);
console.log(selectedCategory);
}
const [selectedCategory, setSelectedCategory] = useState();
I am using it as a separate component in another component
<FAQcategory selectedCategory={selectedCategory} />
any suggestion ? or help what I am doing wrong
Thanks in advance
This might help
const [selectedCategory, setSelectedCategory] = useState(0); // should be item index
const handleCategory = (index) => {
setSelectedCategory(index);
};
const renderItem = ({ item, index }) => (
<View
key={index}
style={
selectedCategory === index
? styles.activeCategory
: styles.categoryContainer
}
>
<TouchableOpacity
....
onPress={() => handleCategory(index)} // send index in param
>
....
</TouchableOpacity>
</View>
);
First, I run a graphql query and then use its results to render some items using the FlatList. This works fine.
On another screen, I run another mutation using apollos refetch option, such that whenever that mutation is successful, the first query will refetch data where ever it is used. The refetching part works fine and I have tested it on other screens. So automatically, the data in WhiteList is updated.
However the friendList is not updated so the FlatList doesn't get updated.
How can I fix this? useEffectcould have been an option but using the custom graphql hook inside it gives me invalid hook call error. What else can I do?
export const WhitelistBar: React.FC = () => {
const [friendList, setFriendList] = useState<Friend[]>();
const [originatorId, setOriginatorId] = useState<number>();
useEffect(() => {
//console.log('friendlist', friendList);
}, [useGetMyProfileQuery]);
const _onCompleted = (data) => {
console.log('running', data);
let DATA = data.me.friends.nodes.map(
(item: {
id: number;
firstName: string;
rating: number;
vehicles: Vehicle[];
friends: UserConnection;
}) => ({
id: item.id,
imageUrl: defaultUrl,
name: item.firstName,
rating: item.rating,
vehicles: item.vehicles,
numberOfFriends: item.friends.totalCount,
}),
);
setFriendList(DATA);
console.log('daattaaa', DATA);
setOriginatorId(data.me.id)
};
const _onError = () => {
let DATA = [
{
id: 1,
imageUrl: defaultUrl,
name: 'Friend',
},
{
id: 2,
imageUrl: defaultUrl,
name: 'Friend',
},
{
id: 3,
imageUrl: defaultUrl,
name: 'Friend',
},
];
setFriendList(DATA);
setOriginatorId(0);
};
const { data } = useGetMyProfileQuery({
onCompleted: _onCompleted,
onError: _onError,
});
return (
<View style={scaledStyles.container}>
<View style={scaledStyles.whiteListBarTitle}>
<Text style={scaledStyles.whiteListBarText}>Meine Freunde</Text>
</View>
<View style={{ flexDirection: 'row' }}>
<FlatList
showsHorizontalScrollIndicator={false}
data={friendList}
horizontal={true}
renderItem={({ item }) => (
<WhitelistItem
title={item.name}
face={item.imageUrl}
numberOfFriends={item.numberOfFriends}
vehicles={item.vehicles}
/>
)}
keyExtractor={(item) => item.id.toString()}
/>
</View>
</View>
);
};
Edit:
I also tried this but this renders nothing for now:
const showFlatList = (friendList: Friend[] | undefined) => {
return (
<FlatList
showsHorizontalScrollIndicator={false}
data={friendList}
horizontal={true}
scrollEnabled
renderItem={({ item }) => (
<WhitelistItem
title={item.name}
face={item.imageUrl}
firstName={item.name}
rating={item.rating}
numberOfFriends={item.numberOfFriends}
vehicles={item.vehicles}
originatorId={originatorId}
/>
)}
keyExtractor={(item) => item.id.toString()}
/>
);
};
useEffect(() => {
showFlatList(friendList);
}, [useGetMyProfileQuery]);
and this in the main return instead of the FlatList:
{data && showFlatList}
Also, tried this but Argument of type '(DATA: any) => void' is not assignable to parameter of type 'EffectCallback'.ts(2345)
useEffect((DATA: any) => {
setFriendList(DATA)
}, [DATA])
You can't use hook as effect dependency - only variables/props/values.
You can use hook inside effect function (body).
If it is a component dependent on other (parent's) data/values - pass them as prop and use in effect dependency.
You would think that this would be the easiest thing in the world to find an example of but everthing I found online has always had a JSON file as the data source so there are keys to reference
stuff like this...eg item.name etc
const list = [
{
name: 'Amy Farha',
subtitle: 'Vice President'
},
{
name: 'Chris Jackson',
avatar_url: 'https://s3.amazonaws.com/uifaces/faces/twitter/adhamdannaway/128.jpg',
subtitle: 'Vice Chairman'
},
... // more items
]
keyExtractor = (item, index) => index.toString()
renderItem = ({ item }) => (
<ListItem
title={item.name}
subtitle={item.subtitle}
leftAvatar={{
source: item.avatar_url && { uri: item.avatar_url },
title: item.name[0]
}}
/>
)
My data source is just a plain single dimension array, no keys or identifiers of any kind. The data is what it is and wont be changed
data = ['value1','value2','value3',...]
The code I have so far (which obviously doesnt work) is
import React, { Component } from 'react'
import { FlatList, ListItem } from 'react-native'
<FlatList
data={this.state.data}
renderItem={({ item }) => (
<ListItem
key={item.id}
/>
)}
/>
Im guessing I need to use the keyExtractor somewhere to generate an id but how do I reference the actual values in the data to display them if it doesnt have an identifier in the data like a json would?
Try this:
<FlatList
data={this.state.data}
keyExtractor={item => item}
renderItem={({ item }) => (
<ListItem
title={item}
/>
)}
/>
const ListItem = ({ title }) => (
<View>
<Text>{title}</Text>
...
</View>
);