I wasn't able to understand why it does not render the contents of dummyData when I use it with renderItem arrow function but it works with when I pass {item}) => <Text style={styles.item}>{item.key}</Text> to renderItem props directly
const HomeScreen = ({ navigation }) => {
const renderItem = ({item}) => {
<Text style={styles.item}>{item.key}</Text>
}
dataUtils.fetchData();
return(
<View style={styles.container}>
<FlatList
data={dummyData}
renderItem={renderItem}
keyExtractor={item => item.id}
/>
</View>
);
};
You are missing a return statement. Delete brackets or add a return.
Like this:
const renderItem = ({item}) =>
<Text style={styles.item}>{item.key}</Text>
Or:
const renderItem = ({item}) => {
return <Text style={styles.item}>{item.key}</Text>
}
Related
Using react native with typescript and redux toolkit
Hi I'm bothering with render a list of messages via FlatList. By ScrollView everything rendering good but I need to implement infiniti scroll. So I'm doing something like this
const MessagesScreen = () => {
const companyId = useAppSelector(getCompanyId);
const userId = useAppSelector(getUserId);
const {
data: messages,
isLoading,
refetch
} = useGetMessagesQuery({ userId, companyId });
useFocusEffect(refetch);
return (
<FlatList
data={messages}
renderItem={() => {
<Messages messages={messages} />;
}}
/>
);
};
In return() I'm trying to render FlatList with component Messages which is down here:
const Messages = ({ messages }: { messages: Message[] }) => {
const navigation =
useNavigation<RootStackScreenProps<'DrawerNavigator'>['navigation']>();
const { colors } = useTheme();
return (
<View style={styles.container}>
{messages.map(message => {
const createdAt = message.created_at;
const isRead = message.read;
const icon = isRead ? 'email-open-outline' : 'email-outline';
const onClick = () => {
navigation.navigate('Message', {
messageId: message.id
});
};
return (
<TouchableOpacity key={message.id} onPress={onClick}>
<View
style={[styles.message, { borderBottomColor: colors.separator }]}
>
<View style={styles.iconPart}>
<Icon
name={icon}
type="material-community"
style={
isRead
? { color: colors.separator }
: { color: colors.inputFocus }
}
size={24}
></Icon>
</View>
<View style={styles.bodyPart}>
<Text
numberOfLines={1}
style={[isRead ? styles.readSubject : styles.unReadSubject]}
>
{message.subject}
</Text>
<Text
numberOfLines={1}
style={[isRead ? styles.readBody : styles.unReadBody]}
>
{message.body}
</Text>
</View>
<View style={styles.datePart}>
<Text style={{ color: colors.shadow }}>
{dayjs(createdAt).fromNow()}
</Text>
</View>
</View>
</TouchableOpacity>
);
})}
</View>
);
};
Actually behaviour is just rendering white screen with error
Possible Unhandled Promise Rejection (id: 17):
Error: Objects are not valid as a React child (found: object with keys {id, msg_type, created_at, subject, body, author, company_id, read}). If you meant to render a collection of children, use an array instead.
there is problem with your call back function:
you are not returning Messages component
1:Remove curly braces
return (
<FlatList
data={messages}
renderItem={() => <Messages messages={messages}/> }
/>
);
2:Add return statement
return (
<FlatList
data={messages}
renderItem={() => {
return <Messages messages={messages} />;
}}
/>
);
Couple things:
You're using the renderItem callback incorrectly:
<FlatList
data={messages}
renderItem={() => {
// ^ ignoring the renderItem props
return <Messages messages={messages} />;
}}
/>
Here, for each item in the messages array, you're rendering a component and passing all the messages into it. So you'll get repeated elements.
The renderItem callback is passed {item, index} where item is the CURRENT item in the array (index is the index into the array)
See docs here:
https://reactnative.dev/docs/flatlist
The usual thing is the renderItem callback renders ONE item at a time, like this:
<FlatList
data={messages}
renderItem={({item}) => {
return <Message message={item} />;
}}
/>
e.g. I'd make a <Message/> component that renders one item only.
I'm trying to display a bunch of data i have on firebase in a flatlist, I don't really know where the problem is right now, i've tried physically filling the array out and that works but it doesn't when i get the data from firebase. I do see that im getting the data on the console log but it's not getting displayed.
function Squad() {
const gk = [];
db.collection('squad').orderBy('position').get().then(snapshot => {
snapshot.forEach(doc => {
const playerObject = doc.data();
gk.push({name: playerObject.name, number: playerObject.number});
console.log(gk);
});
});
const Item = ({ name, number }) => (
<View style={styles.item}>
<Text style={styles.itemText}>{number} - {name}</Text>
</View>
);
const renderItem = ({ item }) => (
<Item name={item.name} number={item.number} />
)
return(
<View>
<View style={styles.bar}>
<Text style={styles.barText}>goalkeeper</Text>
</View>
<FlatList
data={gk}
renderItem={renderItem}
keyExtractor={item => item.id}
/>
</View>
)
}
You can get the info on mount in an async function, then store it in a stateful array using hooks.
function Squad() {
const [gk, setGk] = useState([]);
const getSquad = async () => {
const ref = db.collection('squad').orderBy('position');
const doc = await ref.get();
const playerObject = doc.data();
const newGk = [...gk, {name: playerObject.name, number: playerObject.number}];
setGk(newGk);
}
useEffect(() => {
getSquad();
}, [])
const Item = ({ name, number }) => (
<View style={styles.item}>
<Text style={styles.itemText}>{number} - {name}</Text>
</View>
);
const renderItem = ({ item }) => (
<Item name={item.name} number={item.number} />
)
return(
<View>
<View style={styles.bar}>
<Text style={styles.barText}>goalkeeper</Text>
</View>
<FlatList
data={gk}
renderItem={renderItem}
keyExtractor={item => item.id}
/>
</View>
)
}
Start by creating a useEffect hook to act as a componentWillMount function to call the method when the compnent is ready. I have also included catch block in order to show any errors that might occur.
import React, { useState, useEffect } from "react";
const Squad = () => {
const [gk, setGK] = useState([]);
useEffect(() => {
getSquadData();
});
const getSquadData = () => {
db.collection("squad")
.orderBy("position")
.get()
.then((snapshot) => {
let myData = [];
snapshot.forEach((doc) => {
const playerObject = doc.data();
myData.push({
id: playerObject.id,
name: playerObject.name,
number: playerObject.number,
});
});
setGK(myData);
})
.catch((error) => {
console.log("Error getting data: ", error);
});
};
const Item = ({ name, number }) => (
<View style={styles.item}>
<Text style={styles.itemText}>
{number} - {name}
</Text>
</View>
);
const renderItem = ({ item }) => (
<Item name={item.name} number={item.number} />
);
return (
<View>
<View style={styles.bar}>
<Text style={styles.barText}>goalkeeper</Text>
</View>
<FlatList
data={gk}
renderItem={renderItem}
keyExtractor={(item) => item.id}
/>
</View>
);
};
export default Squad;
So if you are seeing error logs in your console, check your collection in firestore, and also check the rules in your firestore.
I have DATA - array of arrays(important) with items, and trying to create flatlist:
<FlatList
data={DATA}
renderItem={renderItem}
keyExtractor={(item) => item.id}
/>
so when I was mapping items in renderItem like that:
items.map((item, index) => {
return (
<View style={styles.item} key={index}>
</View>
);
});
I have received Nothing was return error. What am I supposed to do?
Ed. https://snack.expo.io/#komarnytskiyivan/gnarly-waffle - code sandbox
You need to change your Item component like so :-
const Item = ({ items }) => {
return items.map((item, index) => {
return (<View style={styles.item} key={index}>
<Text style={styles.title}>{item.title}</Text>
</View>);
});
};
Changes :-
return the new array from .map
return the <View>...</View> component
remove the item && index check. Will fail when index is equal to 0.
In your Item you have this:
const Item = ({ items }) => {
console.log(items);
items.map((item, index) => {
console.log(item)
if (item && index) {
return (
<View style={styles.item} key={index}>
<Text style={styles.title}>{item.title}</Text>
</View>
);
}
});
};
You need to return from the items.map so it is return items.map
<FlatList
data={DATA}
renderItem={renderItem}
keyExtractor={(item) => item.id}
/>
Use renderItem like this
renderItem = ({ item }) => {
return (
<View style={styles.item} key={index}>
// Your view here
</View>
);
};
I'm making an app in React Native. There are three components I'm currently concerned with:
AllList.js: A screen comprised of a search bar and a FlatList of RowCard.js instances.
RowCard.js: a custom TouchableHighlight component that displays an item from an API, and displays DrinkPopup.js when tapped by using a state stored in AllList.js.
DrinkPopup.js: A custom Modal component that needs to take an ID from whichever RowCard is tapped and use it to make an API call to get its own data.
I can't figure out how to take the ID from the RowCard or FlatList and pass it to DrinkPopup - how should I do it?
Relevant code for AllList:
export default function AllList() {
const [isLoading, setLoading] = useState(true);
const [drinkData,setDrinkData] = useState([]);
const [searchValue, onChangeText] = useState(''); //needed for search
const [reloading, setReloading] = useState(false);
const [modalVisible, setModalVisible] = useState(false); //normal modal visibility handler
useEffect (() => {
fetch('https://www.thecocktaildb.com/api/json/v1/1/search.php?s=' + searchValue)
.then((response) => response.json())
.then((json) => setDrinkData(json.drinks))
.catch((error) => console.error(error))
.finally(() => setLoading(false));
},[drinkData]);
return (
<View style = {styles.screen}>
<View style = {styles.searchSection}>
<TextInput
placeholder="Search Drinks..."
style={styles.input}
onChangeText={text => onChangeText(text)}
value={searchValue}/>
</View>
<FlatList
data={drinkData}
keyExtractor={({ idDrink }, index) => idDrink}
removeClippedSubviews={true}
initialNumToRender={5}
renderItem={({ item }) => (
<RowCard id={item.idDrink} image={item.strDrinkThumb} title={item.strDrink} alcontent={item.strAlcoholic}
ingredient1={item.strIngredient1} ingredient2={item.strIngredient2} ingredient3={item.strIngredient3} setModalVisible={setModalVisible}
/>
)}
extraData={reloading}
/>
<DrinkPopup modalVisible={modalVisible} setModalVisible={setModalVisible}/>
</View>
);
};
Relevant code for RowCard:
const RowCard = (props) => {
return(
<TouchableHighlight
style={styles.rowCard}
activeOpacity={0.6}
underlayColor={"white"}
onPress={() => {props.setModalVisible(true) }}
>
<View style={styles.rowCard}>
<Image source={{uri: props.image, width: 150, height: 150}} />
<View style={styles.textBox}>
<Text style={styles.titleText}>{props.title}</Text>
<Text style={styles.ingredient}> Main ingredients: {props.ingredient1}, {props.ingredient2}, {props.ingredient3} </Text>
</View>
</View>
</TouchableHighlight>
)
};
Relevant code for DrinkPopup:
const DrinkPopup = (props) => {
return(
<Modal isVisible={props.modalVisible}
onBackdropPress={()=>{props.setModalVisible(false)}} //allows closing modal by tapping outside it or back button
onBackButtonPress={()=>{props.setModalVisible(false)}}
animationIn={"slideInUp"}>
<View style={styles.infocard}>
<View style={styles.titleBox}>
<Text style={styles.header}>I HAVE NO IDEA WHAT YOU PICKED</Text>
</View>
</View>
</Modal>
)
}
I'm really new to JS and React. I get this error:
Invalid Hook Call
when I try to make a component appear and disappear when another component is clicked. This is my code:
const RenderList = ({data}) => {
return data.map((option, index) => {
return <Item title={option}/>
});
};
const Header = ({ title, style, press }) => (
<TouchableHighlight onPress={press}>
<Text style={style} >{title}</Text>
</TouchableHighlight>
)
const RenderItem = ( {item} ) => {
console.log(styles)
let dataToShow;
const [listState, setListState] = useState(true);
if (listState){
dataToShow = <RenderList data={item.data}/>
} else {
dataToShow = <Text/>
}
return (
<View style={styles.section}>
<Header title={item.title} style={styles.header} press={setListState(!listState)}/>
{dataToShow}
</View>
)}
EDIT
RenderItem is used in a flat list element as a function. (From what I understand)
const SettingsSection = (props) => {
const db = props.data;
return(
<View>
<FlatList
style={styles.sectionList}
data={db}
renderItem={RenderItem}
keyExtractor={item=>item.title}
ItemSeparatorComponent={FlatListItemSeparator}
/>
</View>
);
}
renderItem, as the name suggests, is a render prop, and as such is called directly (like so: renderItem({item})), not instantiated as a component (like so: <RenderItem item={item}/>).
This translates to React not creating the appropriate rendering "context" for hooks to work. You can make sure your RenderItem function is instantiated as a component by using it like this on the render prop:
<FlatList
style={styles.sectionList}
data={db}
renderItem={item => <RenderItem {...item}/>} // see here!
keyExtractor={item=>item.title}
ItemSeparatorComponent={FlatListItemSeparator}
/>
That way, RenderItem is treated as a component and thus can use hooks.
I think problem is occurring due to setListState(!listState) with press. I suggest you to wrap your state changing method into a function. Because onPress accepts only function type but you are giving it a return statement from hooks.
const RenderList = ({data}) => {
return data.map((option, index) => {
return <Item title={option}/>
});
};
const Header = ({ title, style, press }) => (
<TouchableHighlight onPress={press}>
<Text style={style} >{title}</Text>
</TouchableHighlight>
)
const RenderItem = ( {item} ) => {
console.log(styles)
let dataToShow;
const [listState, setListState] = useState(true);
if (listState){
dataToShow = <RenderList data={item.data}/>
} else {
dataToShow = <Text/>
}
return (
<View style={styles.section}>
<Header
title={item.title}
style={styles.header}
press={()=>{
setListState(!listState)
}}
/>
{dataToShow}
</View>
)}