React Native: Lodash Map - react-native

I'm new in react native and I want make data function into view.
my function looks like this
renderTest = () => {
<FlatList
onEndReached={0}
onEndReached={() => this.handleEnd()}
>
{_.map(this.state.leads, (leads, index) => {
return (
<Text key={index}>{leads.full_name}</Text>
)
})}
</FlatList>
}
and my View to pass the value of the function
<View style={{flexDirection: 'row'}}>
{this.renderTest()}
</View>
I don't have any idea what's the problem all I just want is to render the value. I hope could someone help me.
edited

Since you already are in react world you can simply use the Array.map:
renderTest = () => {
<FlatList onEndReached={() => this.handleEnd()}>
{this.state.leads.map((lead, index) => {
return (<Text key={index}>{lead.full_name}</Text>)
})}
</FlatList>
}
As long as this.state.leads is an array.
But bottom line is no lodash is needed here.

As seen by your comment on Akrion's answer, I'm guessing you haven't defined the renderTest function inside the component. Another possibility is that you are using a stateless component in which case you cannot access this

I'm just not sure you can define a FlatList like that one. I mean, I think you're missing the return of the function and you have to pass the property data to FlatList this way:
renderTest = () => {
return (
<FlatList
onEndReached={0}
onEndReached={() => this.handleEnd()}
data={this.state.leads}
renderItem={(lead, index) => <Text key={index}>{lead.full_name}</Text>}
/>
)
}
Let me know if this can solve your issue :)

Related

react-native : how to sort data before screen is drawn?

I call data by using useQuery and gql.
const SEE_ALL_FEED_ORDER = gql`
query seeAllFeedOrder {
seeAllFeedOrder {
id
name
avatar
directFeedNumber
}
}
`;
const { data: allFeedData, loading: allFeedDataLoading,
refetch: allFeedRefetch } = useQuery(SEE_ALL_FEED_ORDER);
I named result data as allFeedData as above.
And I need to sort this allFeedData before screen is shown up.
So I use useEffect and useState.
const [flatlistdata, setFlatlistdata] = useState([]);
useEffect(() => {
if (!allFeedDataLoading) {
setFlatlistdata(
[...allFeedData.seeAllFeedOrder].sort(function (a, b) {
return b.directFeedNumber - a.directFeedNumber;
})
);
}
}, []);
So if this query loading is finished, then I put sorted data to flatlistdata by using setFlatlistdata.
And with this flatlistdata, I run flatlist.
<FlatList
data={flatlistdata}
keyExtractor={(item) => item.id}
renderItem={RankRow}
refreshing={refreshing}
onRefresh={refresh}
/>
But when I click screen, undefined is not an object(evaluating 'allFeedData.seeAllFeedOrder' error comes.
which means I couldn't call allFeedData.
I think this might happen screen is drawn before data is sorted? is that right?
So I also give condition to Flatlist as below.
flatlistdata === [] ? (
<View>
<ActivityIndicator size={30}></ActivityIndicator>
</View>
) :
<FlatList
data={flatlistdata}
keyExtractor={(item) => item.id}
renderItem={RankRow}
refreshing={refreshing}
onRefresh={refresh}
/> }
or
allFeedDataLoading ? (
<View>
<ActivityIndicator size={30}></ActivityIndicator>
</View>
) :
<FlatList
data={flatlistdata}
keyExtractor={(item) => item.id}
renderItem={RankRow}
refreshing={refreshing}
onRefresh={refresh}
/> }
I intended to draw ActivityIndicator screen first before data is sorted and put to flatlistdata and then proper screen show up, but it throws same error.
What is the problem? and how can I fix this ?
So I use useEffect and useState.
why would you need that? the sorted data is derived state that doesn't need to be managed separately:
const { data: allFeedData } = useQuery(SEE_ALL_FEED_ORDER);
const sortedData = allFeedData ? [...allFeedData.seeAllFeedOrder].sort(...) : []
then just pass sortedData to the FlatList. If it turns out that sorting is slow or that referential identity is important, you can wrap the sorting in useMemo. I also have a blog post on this topic: https://tkdodo.eu/blog/dont-over-use-state

React native FlatList not rerendering when data prop changes to empty array

I have a FlatList with a data prop pulling from Redux
render() {
return (
<View>
<FlatList
data={this.props.arrayOfPlacesFromRedux}
renderItem={({item}) => {.......
Whenever I dispatch changes to arrayOfPlacesFromRedux(i.e. adding or removing children), the FlatList rerenders....UNLESS I remove all children from array (i.e. make length zero).When arrayOfPlacesFromRedux changes from a positive length to a length of zero, the FlatList does not rerender.....however all other types of changes to array do indeed cause FlatList to rerender
UPDATE 02/27
Below is my reducer used to update Redux arrayOfPlacesFromRedux
const reducer = (state = initialState, action) => {
switch (action.type) {
case UPDATE_PLACES_ARRAY:
return {...state, arrayOfPlaces: action.payload};
default:
return state;
}
};
In the situation noted above when FlatList does not rerender.....action.payload is an empty array
The question is missing some important piece of code.
React as well as Redux need arrays reference to change, meaning for a component to reRender on state change, the array references needs to change.
Live demo at https://snack.expo.dev/RrFFxfeWY
Here is the most interesting parts:
If you have a basic component as below:
const MyList = () => {
const [data, setData] = React.useState([
'#FF0000',
'#FF8000',
'#FFFF00',
]);
return (
<>
<Text>List poping is not working</Text>
<FlatList
data={data}
renderItem={({ item }) => (
<Pressable
onPress={() => {
data.pop(); // Does not work because we are not changing it's ref
}}
style={{ backgroundColor: item, padding: 8 }}>
<Text>{item}</Text>
</Pressable>
)}
/>
</>
);
};
The data need to have a new array reference as below. data2.filter(..) will return a new array, we are not changing the data2 base values, just creating a new array with one item less.
const MyList = () => {
const [data2, setData2] = React.useState([
'#00FFFF',
'#0080FF',
'#0000FF',
]);
return (
<>
<Text>List WORKING!</Text>
<FlatList
data={data2}
renderItem={({ item }) => (
<Pressable
onPress={() => {
setData2(data2.filter(dataItem => dataItem !== item)) // works
//setData2([]); // Also works
}}
style={{ backgroundColor: item, padding: 8 }}>
<Text>{item}</Text>
</Pressable>
)}
/>
</>
);
};
A library like Immer.js simplify the manipulation of states to mutate the object, and immer will created a new reference for you.
Oh no rookie mistake that wasted everyones time!!
I was implementing shouldComponentUpdate method that was stopping Flatlist rendering :(
Thanks for all for the answers
You may need to use ListEmptyComponent, which is a prop that comes with FlatList, src.
Honestly, I'm not sure why it does not re-render when you update your state, or why they added a specific function/prop to render when the array is empty, but it's clear from the docs that this is what's needed.
You can do something like this:
<SafeAreaView style={styles.container}>
<FlatList
data={DATA}
renderItem={renderItem}
keyExtractor={(item) => item.id}
extraData={selectedId}
--> ListEmptyComponent={() => <MyComponent />}
/>
</SafeAreaView>

how to use flatlist in react native with fetch api

I am using react native with laravel back end so i just want to load list from laravel so for that i code like that
constructor() {
super();
this.state = {
data:[
{
student_name: '',
class:'',
section:'',
},
],
}
//id is also in state and i get it's value from async storage
fetch('http://192.1.1.:8000/api/students/' + this.state.id, {
method: 'get',
})
.then((response) => response.json())
.then((responseJson) => {
if (responseJson.message === 'success') {
responseJson.data.map((userData) => {
this.setState({student_name: userData.student_name})
this.setState({class: userData.class})
this.setState({section: userData.section})
});
}
that's how i get record from laravel and update state in react native. but when i use flat list in react native it throw me that error
VirtualizedList: missing keys for items, make sure to specify a key or id property on each item or provide a custom keyExtractor.
My react native view is like that
<View>
<Text style={{fontSize:50}}>FlatList</Text>
<FlatList
data={this.state.data}
renderItem={({item})=><Text style={{fontSize:50}}>{item.student_name}</Text>}
/>
</View>
Can you try this?
keyExtractor={(item, index) => index.toString()}
You have to add a unique key (or id) prop for Text element in FlatList.
Supposing that your this.state.data items have an id, you could write something like:
<View>
<Text style={{fontSize:50}}>FlatList</Text>
<FlatList
data={this.state.data}
renderItem={({item})=><Text key={item.id} style={{fontSize:50}}>{item.student_name}</Text>}
/>
</View>
Alternatively, you could add keyExtractor to FlatList in this way:
<View>
<Text style={{fontSize:50}}>FlatList</Text>
<FlatList
data={this.state.data}
renderItem={({item})=><Text style={{fontSize:50}}>{item.student_name}</Text>}
keyExtractor={(item) => item.id}
/>
</View>
in Flatlist you have been need on key parameter . you must get that from list , like id parameter .
add this parameter to flatlist item
style={styles.flatListStyle}
data={this.state.bestSuggester}
key={item => item.Rank}
Its because you are missing keyExtractor
Replace your code with this:
<View>
<Text style={{fontSize:50}}>FlatList</Text>
<FlatList
data={this.state.data}
keyExtractor={(item, index) => String(index)}
renderItem={({item})=><Text style={{fontSize:50}}>{item.student_name}</Text>}
/>
</View>

Make VirtualizedList show as Grid

I'm trying to make something like this:
The problem: The project was built with immutablejs and according to React Native Docs, I can't use FlatList thus I can't use numColumns props feature of that component.
AFAIK, my only choice is to use VirtualizedList as the docs points out, but I can't figure out how to display the cells as a grid as shown above.
I've already tried to add style props in both cell and view wrapper, but none of the code used to align the cells, like the picture I posted, is ignored. In fact it was showing perfect when I was using ScrollView, but due the HUGE lag I'm moving the code to VirtualizedList.
Any help? Anything would be welcome, I already digged a lot on Google but I can't find anything about this.
Some sample code:
<View>
<VirtualizedList
data={props.schedules}
getItem={(data, index) => data.get(index)}
getItemCount={(data) => data.size}
keyExtractor={(item, index) => index.toString()}
CellRendererComponent={({children, item}) => {
return (
<View style={{any flexbox code gets ignored here}}>
{children}
</View>
)}}
renderItem={({ item, index }) => (
<Text style={{also here}} key={index}>{item.get('schedule')}</Text>
)}
/>
</View>
Answering my own question:
I got it working by copying the FlatList.js source code from react-native repo.
Here's an example code:
<VirtualizedList
data={props.schedules}
getItem={(data, index) => {
let items = []
for (let i = 0; i < 4; i++) {
const item = data.get(index * 4 + i)
item && items.push(item)
}
return items
}}
getItemCount={(data) => data.size}
keyExtractor={(item, index) => index.toString()}
renderItem={({item, index}) => {
return (
<View key={index} style={{flexDirection: 'row'}}>
{item.map((elem, i) => (
<View key={i}>
<Text key={i}>{elem.get('horario')}</Text>
</View>
))}
</View>
)
}}
/>
The number 4 is for the number of columns. The key parts are in the getItem and adding flexDirection: 'row' at renderItem in the View component.

ScrollToEnd after update data for Flatlist

I'm making a chat box with Flatlist. I want to add a new item to data then scroll to bottom of list. I use scrollToEnd method but it did not work. How can I do this?
<FlatList
ref="flatList"
data={this.state.data}
extraData = {this.state}
renderItem={({item}) => <Text style={styles.chatFlatListItem}>{item.chat}</Text>}
/>
AddChat(_chat){
var arr = this.state.data;
arr.push({key: arr.length, chat: _chat});
var _data = {};
_data["data"] = arr;
this.setState(_data);
this.refs.flatList.scrollToEnd();
}
I found a better solution,scrollToEnd() is not working because it is triggered before the change is made to the FlatList.
Since it inherits from ScrollView the best way here is to call scrollToEnd() in onContentSizeChange like so :
<FlatList
ref = "flatList"
onContentSizeChange={()=> this.refs.flatList.scrollToEnd()} />
Thanks #Kernael, just add a timeout like so:
setTimeout(() => this.refs.flatList.scrollToEnd(), 200)
const flatList = React.useRef(null)
<FlatList
ref={flatList}
onContentSizeChange={() => {
flatList.current.scrollToEnd();
}}
data={this.state.data}
extraData = {this.state}
renderItem={({item}) => <Text style={styles.chatFlatListItem}>{item.chat}</Text>}
/>
try this,it works.
My issue here was that scrollToEnd() worked fine on mobile but on web it always scrolled to the top. Probably because I have elements with different size in the FlatList and couldn't define getItemLayout. But thanks to the accepted answer here I solved it. Just with different approach.
const ref = React.useRef<FlatList>();
function handleScrollToEnd(width, height) {
if (ref.current) {
ref.current.scrollToOffset({offset: height});
}
}
<FlatList
ref={ref}
onContentSizeChange={handleScrollToEnd}
/>
This works great on both the mobile and web. Hope it helps to somebody.
Change your code as below. The ref is modified and It's better to use getItemLayout in your FlatList according to this.
AddChat(_chat){
var arr = this.state.data;
arr.push({key: arr.length, chat: _chat});
var _data = {};
_data["data"] = arr;
this.setState(_data);
this.flatList.scrollToEnd();
}
<FlatList
ref={elm => this.flatList = elm}
data={this.state.data}
extraData = {this.state}
renderItem={({item}) => <Text style={styles.chatFlatListItem}>{item.chat}</Text>}
getItemLayout={(data, index) => (
{length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index}
)}
/>
Note: Replace the ITEM_HEIGHT with the real value of height of your list items.
Try to use inverted prop on Fatlist itself
Pass your data like this [...data].reverse()
If you are at the middle of list and you need to scroll to end when a new item is added, just use:
ref => flatlistRef.current?.scrollToOffset({offset:0})
seems caused by this line
https://github.com/facebook/react-native/blob/3da3d82320bd035c6bd361a82ea12a70dba4e851/Libraries/Lists/VirtualizedList.js#L1573
when use trigger scrollToEnd, frame.offset is 0
https://github.com/facebook/react-native/blob/3da3d82320bd035c6bd361a82ea12a70dba4e851/Libraries/Lists/VirtualizedList.js#L390
if you wait 1 second, _onContentSize changes and frame.offset is valorized (for ex. 1200 px).
Related post https://github.com/facebook/react-native/issues/30373#issuecomment-1176199466
Simply add a loader before Flatlist renders. For example:
const flatListRef = useRef(null);
const [messages, setMessages] = useState([]);
if(!messages.length){
return <Loader />
}
return (
<View style={styles.messagesContainer}>
<FlatList
ref={flatListRef}
data={messages}
onContentSizeChange={() => {
if (flatListRef.current) {
flatListRef?.current?.scrollToEnd();
}
}}
renderItem={({item, index}) => {
return (
<DisplayMessages
message={item}
index={index}
/>
);
}}
/>
</View>