I tried using Flatlist in order to respond to one of my issue but I got an error on the data= of my Flatlist . I don't rreally undersstand the issue and the error message is not helping ( No overload matches this call. Overload 1 of 2, '(props: FlatListProps<any> | Readonly<FlatListProps<any>>): FlatList<any>', gave the following error. Type '{ id: string; name: string; data: string[]; description: string[]; }' is missing the following properties from type 'readonly any[]': length, concat, join, slice, and 19 more. )
I used flatlist because of this : React Native : Conditional Views.
Here is my code:
<View style={{ flex: 10}}>
{letter.map((letter) => {
const isleetergotdata = letter.data?.length > 0;
if (!isleetergotdata) {
return null;
}
return (
<View
style={{
flex: 1,
flexDirection: "row",
}}
>
<div
ref={fieldRef}
style={{
marginBottom: 100,
}}
>
<Text
style={{
fontSize: 90,
}}
>
{letter.name}
</Text>
</div>
{letter.data?.map((item, index) => {
if (total !== same) {
return (
<FlatList
data={letter} // HERE IS THE ISSUE
numColumns={2}
keyExtractor={(_, index) => index.toString()}
renderItem={({ item }) => {
return (
<View>
<Text>{letter.description[index]}</Text>
</View>
);
}}
/>
);
}
})}
</View>
);
})}
</View>
And here is my data:
const letter = [
{
id: "1",
name: "A",
data: ["Abricot", "Abricot", "Abricot"],
description: [
"Un abricot est un fruit",
"Un abricot est un fruit",
"Un abricot est un fruit",
],
},
//...
{
id: "5",
name: "E",
data: [],
description: [],
},
//...
];
The problem here is that you are providing letter to the FlatList's data prop, but this is not the global letter variable but the one of the local scope of the outer map function. You have given them the same name. Thus, letter in the map function is an object and not an array.
It seems to me that you want to create a FlatList for each object inside the "global" letter variable and provide the description property of each of these objects (which is an array and you are acessing it via the index which is really not necessary).
Thus, you actually only need to use one map and remove the inner map function. I would also rename the local variable letter to something else.
<View style={{ flex: 10}}>
{letter.map((datum) => {
const isleetergotdata = datum.data?.length > 0;
if (!isleetergotdata) {
return null;
}
return (
<View
style={{
flex: 1,
flexDirection: "row",
}}
>
<div
ref={fieldRef}
style={{
marginBottom: 100,
}}
>
<Text
style={{
fontSize: 90,
}}
>
{datum.name}
</Text>
</div>
if (total !== same) {
return (
<FlatList
data={datum.description}
numColumns={2}
keyExtractor={(_, index) => index.toString()}
renderItem={({ item }) => {
return (
<View>
<Text>{item}</Text>
</View>
);
}}
/>
);
}
)
}
</View>
);
})}
</View>
And to be precise here, remove the letter.data?.map((item, index) part completely.
If you need both data and desription in the same FlatList, then you can still leave the inner map function.
<FlatList
data={datum.data}
numColumns={2}
keyExtractor={(_, index) => index.toString()}
renderItem={({ item, index }) => {
return (
<View>
<Text>{datum.description[index]}</Text>
</View>
);
}}
/>
You can get the data from array.
Step:01 Inside API call.
jsonElement["Letter"]
Step:02
const [dataSource, setDataSource] = useState([]);
Step:03
<FlatList horizontal={true} data={dataSource} />
Step:04
{item.id}
₹ {item.name}
When I run your code I face a different error related to the div tag. React-Native has no dom and if you do not have a custom 'div' component, you can not use the div tag. Even if a 'Div' component is in your project, component names should start with a capital letter.
Related
I have a list of about 28 items, I made it into 2 columns, It works fine but now I want to show only only the first 10 items and the next 10 will be in the second page of a carousel. I have tried to no avail. I need help please.
this is a shorter version of the code.
function CategoriesScreen(props) {
const CATE = [
{Item: "1"},
{Item: "2"},
...
{Item: "28"},
];
return (
<Screen>
<View style={{ paddingTop: 30 }}>
<FlatList
style={{ margin: 5 }}
data={CATE}
numColumns={2}
initialNumToRender={10}
maxToRenderPerBatch={10}
enableEmptySections={true}
keyExtractor={(item, index) => item.id}
contentContainerStyle={{ paddingVertical: 20 }}
renderItem={({ item }) => (
<View style={styles.card}>
...display items
</View>
)}
/>
</View>
</Screen>
);
}
export default CategoriesScreen;
You should use
onEndReached={this.retrieveMore}
read more here : https://reactnative.dev/docs/flatlist#onendreached
I'm trying to set a menu and I'm trying to pass the path to navigation.navigate as navigation.navigate(menu.path) but it doesn't allow me to pass the route in that way.
So I create the item here with the path as selections.name
export default function Menu_1({ navigation }) {
const [selections, setSelections] = useState([
{ title: 'Normas de utilización de la App', key:'1' , name:'Normas'},
{ title: 'Zonas maritimas', key:'2'}
])
When I try to pass that path to navigation.navigate it doesn't allow to me unless I use Navigation.navigate('Normas')
return (
<View style={globalStyles.container}>
<FlatList
data={selections}
renderItem={({ item })=>(
<TouchableOpacity onPress={() => navigation.navigate()}>
<Card>
<Image
style={{
width: 100,
height: 50,
}}
source={images.images_menu[item.key]} />
<Text style={globalStyles.titleCard}> {item.title} </Text>
</Card>
</TouchableOpacity>
)}
/>
</View>
)
}
is there a way to pass the string from selections.name to navigation.navigate?
If I understood this correctly, you can access the selections.name in the item param of your renderItem prop, like this:
return (
<View style={globalStyles.container}>
<FlatList
data={selections}
renderItem={({ item })=>(
<TouchableOpacity onPress={() => navigation.navigate(item.name)}> // item.name
<Card>
<Image
style={{
width: 100,
height: 50,
}}
source={images.images_menu[item.key]} />
<Text style={globalStyles.titleCard}> {item.title} </Text>
</Card>
</TouchableOpacity>
)}
/>
</View>
)
You can also switch this to whatever you want, navigate(item.key), navigate(item.foo). Just make sure you are keeping the values here
const [selections, setSelections] = useState([
{ title: 'Normas de utilización de la App', key:'1' , name:'Normas'},
{ title: 'Zonas maritimas', key:'2', name: 'Foo'} // missing name
])
Please help my deal with unnecessary re-renders when having two flatlists on the same screen
My screen requires two flatlists-
For HOSTS
For QUEUE
When the component mounts, I get data from the api call like this-
{
"hosts": [{"id":"1", "name":"kyouma"},...],
"queue": [{"id":"99", "name":"eren"},...]
}
Now what I do is I store my hosts and queue separately in my redux store like this-
this.props.dispatch({
type: GET_ROOM_HOSTS,
payload: info['hosts']
})
this.props.dispatch({
type: GET_ROOM_QUEUE,
payload: info['queue']
})
where info is the object received from the api call as shown above. Now I mapStateToProps these two from the redux store to the default screen component such that-
this.props.roomQueue is for queue and
this.props.roomHosts is for hosts
My FlatList's are like this-
<FlatList
data={this.props.roomQueue}
horizontal={true}
keyExtractor = {item => item.id}
renderItem({item} => {
return(
<customComponent (with suitable props) ..../>
)
})
/>
<FlatList
data={this.props.roomHosts}
numColumns={3}
keyExtractor = {item => item.id}
renderItem({item} => {
return(
<customComponent (with suitable props) ..../>
)
})
/>
PLEASE NOTE that both the FlatList's are present in the same Component (React.Component) of the screen and are displayed at different parts of the screen(queue at bottom of the screen and hosts at the top of the screen). Also queue and hosts are independent of each other. Screen looks like this
My problem is that even if there is a change in this.props.roomQueue, the FlatList having its data={this.props.roomHosts} get's re-rendered.
How do i prevent this re-render to ensure that only if the FlatList's corresponding data changes, then only will it re-render, otherwise it won't. Do I have to change the way I store queue and hosts? or is there something else?
You can do this with using only one flatlist. Merge your both array's into one and show results from one list.. you can spare them in ui with a type.
This is a genuine procedure of what developers do, cz rendering 2 list in same page and same direction is accually no mean. Your query is valid.
You can add List ListFooterComponent and it will automatically do this for you
<FlatList
contentContainerStyle={{
width: WINDOW_WIDTH,
paddingVertical:WINDOW_WIDTH*0.2,
marginLeft:10
}}
ListFooterComponent={()=> returnYourViewDesignHere()}
columnWrapperStyle={{ flex: 1, justifyContent: "space-around" }}
keyExtractor={(item) => item.id}
onEndReached={() => getPaginationData()}
onEndReachedThreshold={0.0001}
numColumns={3}
showsVerticalScrollIndicator={false}
data={allShows}
renderItem={({ item, index }) => {
return (
<TouchableWithoutFeedback
key={item.index + Math.floor(Math.random() * 1000)}
onPress={() =>
props.navigation.navigate(
item.type == "movie" ? "MovieDetailScreen" : "SeasonDetail",
{
data: item,
object: {
id: item.id,
},
}
)
}
>
<View style={styles.boxContainer}>
<View style={styles.imageBackground}>
<Text style={styles.backgroundText}>KEIN</Text>
<Text
style={[styles.backgroundText, { color: COLOR.primary }]}
>
POSTER
</Text>
</View>
<Image
source={{
uri: item.coverUrl ? item.coverUrl : item.coverPath,
}}
style={styles.imageBox}
resizeMode={"stretch"}
/>
<Text
numberOfLines={2}
ellipsizeMode={"tail"}
style={styles.text}
>
{item.showTitle ? item.showTitle : item.title}
</Text>
{userWatchedList.some((uwl) => uwl.id == item.id) ? (
<TouchableWithoutFeedback
onPress={() =>
isloggedIn
? removeFromUserWatchList(item)
: handleModalVisibility()
}
>
<Image
source={WATCHLIST_CHECKED}
style={{
width: 25,
height: 25,
position: "absolute",
right: 5,
top: 5,
}}
/>
</TouchableWithoutFeedback>
) : (
<TouchableWithoutFeedback
onPress={() =>
isloggedIn
? addToUserWatchList(item)
: handleModalVisibility()
}
>
<Image
source={CIRCLE_UNCHECKED}
style={{
width: 25,
height: 25,
position: "absolute",
right: 5,
top: 5,
}}
/>
</TouchableWithoutFeedback>
)}
</View>
</TouchableWithoutFeedback>
);
}}
/>
I tried to create a DraggableList with some checkboxes, the problem is my component doesn't trigger the "onPress" while the "onLongPress" method working fine...
I tried to remove the "onLongPress" method, but still the same.
Here is my whole component :
class Menus extends React.Component {
state = {
menusItems: global.conf.Menus.map((d, index) => ({
id: d.id,
key: `item-${d.id}`,
label: d.label,
path: d.path,
value: d.value,
backgroundColor: `rgb(${Math.floor(Math.random() * 255)}, ${index *
5}, ${132})`,
})),
};
renderItem = ({item, index, drag, isActive}) => {
return (
<TouchableOpacity
style={{
backgroundColor: isActive ? 'blue' : item.backgroundColor,
alignItems: 'center',
justifyContent: 'center',
}}
onLongPress={drag}
onPress={() => console.log('puff')}>
<Checkbox
status={item.value ? 'checked' : 'unchecked'}
key={item.id}
title={item.label}
/>
</TouchableOpacity>
);
};
return (
<View style={{flex: 1}}>
<DraggableFlatList
data={this.state.menusItems}
renderItem={this.renderItem}
keyExtractor={(item, index) => `draggable-item-${item.key}`}
onDragEnd={({data}) => this.setMenus(data)}
/>
</View>
);
}
}
export default Menus;
I don't know what is wrong ....
friend I Will integrated checked - unchecked in listView. So that When user click on checked then store the data in Array and unchecked then i will remove the data. Its working fine, But the UI Will not updated after checked - unchecked.
<List containerStyle={{marginTop : 0 , borderBottomWidth : 0 , borderBottomColor : 'black', borderTopWidth : 0}}>
<FlatList
data={this.state.list}
renderItem={({ item }) => (
<ListItem containerStyle={{height: 80, backgroundColor : 'transparent', borderBottomWidth : 0, borderTopWidth : 0}}
title={
<View style={styles.titleView}>
<Text style={styles.ratingText}>{item.iWorkerID.vFirstName}</Text>
</View>
}
rightIcon={
<TouchableOpacity onPress = {() => this.selectedWorker(item)} style={{width: 30, height: 30 , marginTop : 10, marginRight : 30}}>
<Image style = {{width: 30, height: 30}} source={this.state.selectedList.includes(item) ? require("./Images/uncheckd.png") : require("./Images/checked.png")}/>
{/* {this.state.selectedList.includes(item) && <Image style = {{width: 30, height: 30}} source={require("./Images/uncheckd.png")}/>}
{!this.state.selectedList.includes(item) && <Image style = {{width: 30, height: 30}} source={require("./Images/checked.png")}/>} */}
</TouchableOpacity>
}
avatar={<Avatar
rounded
medium
containerStyle={{marginLeft: 30}}
source={{uri: Globle.IMAGE_URL+item.vProfileImage}}
activeOpacity={0.7}
/>}
/>
)}
/>
</List>
And on the check/uncheck button, I will add/remove object from array,
selectedWorker = (data) =>{
console.log('data is ', data);
if (!this.state.selectedList.includes(data)) {
// this.setState({ selectedList : [...this.state.selectedList , data]})
this.state.selectedList.push(data);
} else {
var index = this.state.selectedList.indexOf(data);
if (index > -1) {
this.state.selectedList.splice(index, 1);
}
}
this.setState({list : this.state.list})
console.log('selected list' , this.state.selectedList);
}
Main Issue : Want to update image checked/unchecked according to selectedList array, How can i Update item in listView.
What to do inside selectedWorker method.
GIF :
you are using Flatelist inside List, Both are a component to listing items. you can use List or Flatelist, not both.
I hope it will help you..
I try to make Demo similar to that you want.
constructor(props) {
super(props)
this.state = {
list: [
{
id: 1,
name: "Harpal Singh Jadeja",
avtar: "https://cdn.pixabay.com/photo/2016/08/08/09/17/avatar-1577909_960_720.png"
},
{
id: 2,
name: "Kirit Mode",
avtar: "https://cdn.pixabay.com/photo/2016/08/08/09/17/avatar-1577909_960_720.png"
},
{
id: 3,
name: "Rajiv Patil",
avtar: "https://cdn.pixabay.com/photo/2016/08/08/09/17/avatar-1577909_960_720.png"
},
{
id: 4,
name: "Chetan Doctor",
avtar: "https://cdn.pixabay.com/photo/2016/08/08/09/17/avatar-1577909_960_720.png"
}]
};
};
renderListItem = (index, item) => {
return (
<View style={styles.notification_listContainer}>
<Image source={{uri: item.avtar, cache: 'force-cache'}}
style={circleStyle}/>
<View style={{flex: 1, paddingLeft: 10, justifyContent: 'center'}}>
<Label roboto_medium
align='left'
color={Color.TEXT_PRIMARY}
numberOfLines={1}>
{item.name}
</Label>
<Label roboto_medium
small
align='left'
color={Color.TEXT_SECONDARY}
mt={8}>
Programmer
</Label>
</View>
<View style={{justifyContent: 'center'}}>
<TouchableHighlight
style={{
backgroundColor: item.isSelected ? Color.BLACK : Color.TEXT_SECONDARY,
alignItems: 'center',
justifyContent: 'center',
height: 40,
width: 40,
borderRadius: 20
}}
onPress={this.onSelectWorker.bind(this, index, item)} underlayColor={Color.BLACK}>
<Icon name='done'
size={20}
color={Color.WHITE}/>
</TouchableHighlight>
</View>
</View>
);
};
onSelectWorker = (index, item) => {
console.log("Selected index : ", index);
let tempList = this.state.list;
tempList[index].isSelected = tempList[index].isSelected ? false : true
this.setState({
list: tempList
});
};
render() {
return (
<View style={styles.notification_Container}>
<FlatList
data={this.state.list}
renderItem={
({index, item}) => this.renderListItem(index, item)
}
keyExtractor={item => item.id}
extraData={this.state}
/>
</View>
)
}
You need to add a key to your ListItem which is based on a unique id of the item, so that React can distinguish between the items rendered.
When you use index of an array as a key, React will optimize and not render properly. What happens in such a scenario can be explained with an example.
Suppose React renders an array of 10 items and renders 10 components. Suppose the 5th item is then removed. On the next render React will receive an array of 9 items and so React will render 9 components. This will show up as the 10th component getting removed, instead of the 5th, because React has no way of differentiating between the items based on index.
Therefore always use a unique identifier as a key for components that are rendered from an array of items.