How To get 2 Flat list To attract instead of repel - react-native

So I am working on a react native project and have created a modal.
this modal shows the selected and unselected Tasks. Now For this I created a View With two flat lists as shown:
<View style={styles.tasksListContainer}>
<FlatList
data={tasksToShow}
renderItem={(itemData) => {
if (!itemData.item.usedUp) {
return <TaskIsNotBeingUsed itemData={itemData} onTaskSelect={onTaskSelect}/>
}
}}
/>
<FlatList
data={tasksToShow}
renderItem={(itemData) => {
if (itemData.item.usedUp) {
return <TaskIsBeingUsed itemData={itemData}/>
}
}}
/>
</View>
Now the problem is that all the Selected Tasks And None Selected Tasks have some kind of space between them can anyone tell me why?

Because you're using the same array and sometimes return something but sometimes return nothing so there's empty items. You can do it like that:
// Unchecked
<FlatList
data={tasksToShow.filter(itemData => !itemData.usedUp)}
// Checked
<FlatList
data={tasksToShow.filter(itemData => itemData.usedUp)}

You can use style={{ flexGrow: 0 }} on your FlatLists to prevent them from growing to fill the space.
However, this solution has certain drawbacks (eg. both lists scroll independently, and once the top one grows too large the bottom one will be pushed off screen).
A SectionList would be better suited for this purpose:
<View style={styles.tasksListContainer}>
<SectionList
sections={[
{ title: 'being used', data: tasksToShow.filter(el => el.usedUp)},
{ title: 'not being used', data: tasksToShow.filter(el => !el.usedUp)}
]}
renderItem={({ item }) => {
return item.usedUp
? <TaskIsBeingUsed itemData={itemData}/>
: <TaskIsNotBeingUsed itemData={itemData} onTaskSelect={onTaskSelect}/>
}}
/>
</View>

Related

ERROR - VirtualizedLists should never be nested inside plain ScrollViews with the same orientation

I'm working on a react-native app and I have to put a list of object in a Scrollview, so I use the FlatList component to do it. This is the piece of code that generates the error:
<ScrollView contentContainerStyle={style}>
Other components
<FlatList
style={style}
data={data}
scrollEnabled={false}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item, index}) => (somethings)}
/>
Other components
</ScrollView>
The complete error is: VirtualizedLists should never be nested inside plain ScrollViews with the same orientation because it can break windowing and other functionality - use another VirtualizedList-backed container instead.
Avoid using FlatList with the same orientation. Instead, restructure your code like this --
<ScrollView contentContainerStyle={style}>
Other components
{
data.map((item)=> <Somthing item={item}/>)
}
Other components
</ScrollView>
Flatlist has its own ScrollView you can scroll through the list using that so there is no need to put a flatlist into a ScrollView that is why its giving a warning, the both scrollview will clash and one of them (mostly the parent one) works.
The error is self explanatory and it should be in a developers best interest to avoid these kind of things even when it's just a false alarm.
Your particular situation could use the following solution:
<FlatList
data={data}
keyExtractor={(item, index) => `key-${index}`}
ListHeaderComponent={() => (
<SomeComponents>
...Some components those need to be on top of the list
</SomeComponents>
)}
ListFooterComponent={() => (
<SomeComponents>
...Some components those need to be below the list
</SomeComponents>
)}
renderItem={({ item, index}) => (somethings)}
/>
Another note, if you need more complex list that needs header and footer for the list itself, you can try SectionList.
Your component FlatList and ScrollView have the same orientation(vertical), so you need put your component inside a ScrollView with horizontal orientation like this:
<View>
<ScrollView nestedScrollEnabled={true} style={{ width: "100%" }} >
<View>
<ScrollView horizontal={true} style={{ width: "100%" }}>
<FlatList />
</ScrollView>
</View>
</ScrollView>
</View>
Solution 1: Use FlatList props ListHeaderComponent and create all of your page top section in that. Something like this:
This will not show any warning or error.
Solution 2:
Because only parent view will scroll (ScrollView) and not the child FlatList, so to get rid of the warning you can pass a prop scrollEnabled={false} to the FlatList.
If it doesn't go then import LogBox from react-native and write this in your component
useEffect(() => {
LogBox.ignoreLogs(["VirtualizedLists should never be nested"])
}, [])
hopefully, the warning will be removed.
Anyone want to solve this issue can use a custom VirtualizedScrollView like this:
import React from 'react';
import { FlatList } from 'react-native';
const VirtualizedScrollView = props => {
return (
<FlatList
{...props}
data={[]}
keyExtractor={(e, i) => 'dom' + i.toString()}
ListEmptyComponent={null}
renderItem={null}
ListHeaderComponent={() => (
<>{props.children}</>
)}
/>
);
};
export default VirtualizedScrollView;
Then if you use FlatList inside VirtualizedScrollView, it won't get the warning/error.
<VirtualizedScrollView>
<FlatList
/*--- your props ---*/
/>
</VirtualizedScrollView>
There is a npm package where I get this code, you can also use this package
Solution:
I have also encountered same problem with FlatList. Then the package below solved my problem.
'react-native-virtualized-view'
import { ScrollView } from 'react-native-virtualized-view'
if ScrollView is Vertical change Flatlist Horizontal
<ScrollView >
<FlatList
horizontal
data={lenders}
keyExtractor={(_, index) => index}
renderItem={(item) => {
return <Text>item</Text>
}}
/>
You can solve the 2 vertical ones(I'm assuming their side by side, separated with a segemented control?) by using the same flat list and switching out the data when it's switched. If they're just two vertical flat list's one after another use the SectionList.
For the horizontal one you can try putting the Horizontal FlatList in the ListHeaderComponent of the vertical FlatList and see what happens. It can be janky if you use a vertical FlatList in a vertical scroll view but maybe with two different axis it might be ok. The other option is two only show a few items in the horizontal scrollview and have a "Show More".
The last option is too re design/rethink the page so it's not doing so much. On mobile less is more and developers/designers like to get in the mindset of porting desktop thinking onto mobile. Might be worth a shot.
I used the SectionList approach to solve this & wanted to post a code example because I found the Section data required by React Native to be clear but also quite prescriptive.
renderList = ({empty, posts}: {empty: boolean, posts: Array<Object>}) => (
<SectionList
sections={[
{type: 'MAP', data: [{}]}, // Static sections.
{type: 'PROFILE', data: [{}]},
{type: 'POSTS', data: posts} // Dynamic section data replaces the FlatList.
]}
keyExtractor={(item, index) => index}
renderItem={({item, section}) => {
switch (section.type) {
// Different components for each section type.
case 'MAP':
return <MapView />;
case 'PROFILE':
return <Profile />;
case 'POSTS':
return <Post item={item} />;
default:
return null;
}
}}
ItemSeparatorComponent={() => <Separator />}
ListFooterComponent={() => <>{empty && <EmptyList />}</>}
/>
);
What's nice is that the content feels logically quite separate, so you can add sections easily or have different dynamic data sources.
(If you're building a form & want better keyboard handling, you could also try a KeyboardAwareSectionList from react-native-keyboard-aware-scroll-view.)
Flatlist has an integrated scrollview itself, so you can resolve this error by removing ScrollView Component, And let just the Fatlist component
Error ? you are trying to render a FlatList component inside a scrollview component, this is what is throwing the warning.
solution Render the components using Flatlist's ListHeaderComponent={} prop, i.e in your flatlist add the prop as follows
const FlatList_Header = () => {
return (
<View style={{
height: 45,
width: "100%",
backgroundColor: "#00B8D4",
justifyContent: 'center',
alignItems: 'center'
}}
>
<Text style={{ fontSize: 24, color: 'white' }}> Sample FlatList Header </Text>
</View>
);
}
<FlatList
data={BirdsName}
renderItem={({ item }) => <ItemRender name={item.name} />}
keyExtractor={item => item.id}
ItemSeparatorComponent={ItemDivider}
**ListHeaderComponent={FlatList_Header}**
ListHeaderComponentStyle={{ borderBottomColor: 'red', borderBottomWidth: 2 }}
/>
Note the use of the ListHeaderComponent in the code above, that should supress the warning.
Use flatList like this ListHeaderComponent and ListFooterComponent:
<FlatList ListHeaderComponent={
<ScrollView
style={styles.yourstyle}
showsVerticalScrollIndicator={false}
>
<View style={styles.yourstyle}>
</View>
</ScrollView>
}
data={this.state.images}
renderItem={({ item, index }) => {
return (
<View
style={styles.yourstyle}
>
<Image
source={{
uri: item,
}}
style={styles.yourstyle}
resizeMode={"contain"}
/>
<Text
numberOfLines={2}
ellipsizeMode="tail"
style={styles.yourstyle}
>
{item.name}
</Text>
</View>
);
}}
keyExtractor={({ name }, index) => index.toString()}
ListFooterComponent={
<View style={styles.yourstyle}></View>
}
/>
In my case it was happening due to nesting of ScrollView.
Try replacing some of the ScrollView from children components with React.Fragment.
The solution is very simple, please do not put the Flatlist component in the ScrollView.
They both have the same functionality but Flatlist has advantages and is more stable to use.

ReactNative ScrollToIndex too slow

im using react native to create an app for a legal document. i need the document to be scrollable. i rendered it using VirtualizedList.
thing is when i try to use scrollToIndex(index: 'something') performance is too slow.
my list contains about 4000 rendered items (each being about a paragraph long).
is there any way to make this run smoother?
export default function App() {
const scroller = useRef();
return (
<SafeAreaView>
<View style={styles.upperContainer}>
<CustomButton
onPress={() => {
scroller.current.scrollToIndex({ index: 1547 });
}}
/>
</View>
<View style={styles.flatContainer}>
<VirtualizedList
ref={scroller}
data={data}
renderItem={({ item }) => (
<CustomText data={item.content} type={item.type} />
)}
getItem={(data, index) => data[index]}
getItemCount={(data) => data.length}
keyExtractor={(item) => item.number.toString()}
initialNumToRender={4000}
onScrollToIndexFailed={() => {
alert('error');
}}
/>
</View>
</SafeAreaView>
);
}
This question is similar to these ones and maybe you can reference some of the answers and see what works for your case. Basically, you need a pure component to make this work.
Link - Flatlist performance slow
Link - VirtualizedList: You have a large list that is slow to update

Remove item from FlatList in react native

I am using Flat List in react native to select Multiple images from gallery. Now i want to remove some of image before uploading it to server. I am not sure how to perform that action.
Here is my code...
<FlatList
style={{ paddingBottom: 5, paddingTop: 10 }}
data={this.state.imagesAddFile}
keyExtractor={(y, z) => z.toString()}
renderItem={({ item }) => (
<SelectedLayout
ImageName = {item.name}
/>
)}
/>
Here i am getting list of images properly but not sure how to delete image from list please suggest am answer. Thanks
I am using delete function like below method sharing all the code here:
Step 1: Render view in add a TouchableOpacity like below code:
<TouchableOpacity onPress={() => this.deleteAddress(itemData.item._id)}>
<Icon name="trash" style={{paddingLeft: 10,paddingRight:10}} size={20} color="red" />
</TouchableOpacity>
Step 2: Add a confirmation box like below code:
deleteAddress(id) {
Alert.alert(
'Delete Address',
'Are you sure want to delete this address ?',
[
{text: 'Cancel', onPress: () => console.log('Cancel Pressed'), style: 'cancel'},
{text: 'OK', onPress: () => this.deleteAddressDetail(id)},
],
{ cancelable: false }
)
}
Step 3: Call your API or anything here:
deleteAddressDetail(id) {
//Delete api or anything here
//after that call your list so updated state data will render
}
Your updated flatlist code:
<FlatList
data={this.state.addressList}
keyExtractor={this._keyExtractor}
extraData={this.state}
renderItem={this.renderRowItem}
/>
Use render item like below code:
renderRowItem = (itemData) => {
<TouchableOpacity onPress={() => this.deleteAddress(itemData.item._id)}>
<Icon name="trash" style={{paddingLeft: 10,paddingRight:10}} size={20} color="red" />
</TouchableOpacity>
}
Well, you could remove the desired item based on it's index.start with modifying the flatList
<FlatList
style={{ paddingBottom: 5, paddingTop: 10 }}
data={this.state.imagesAddFile}
keyExtractor={(y, z) => z.toString()}
renderItem={({ item,index }) => (
<SelectedLayout
onPress={(index) =>this.removeItemFromList(index)}
ImageName = {item.name}
/>
)}
/>
you should wrap the component SelectedLayout inside a TouchableOpacity in order to implement onPress or whatever way you like it. if you could provide me with it's code I could show you.
now the removeItemFromList Implementation we're gonna use splice to remove it from imagesAddFile state.
removeItemFromList(index){
let newimagesAddFile = this.state.imagesAddFile;
newimagesAddFile.splice(index,1); //to remove a single item starting at index
this.setState({imagesAddFile:newimagesAddFile})
}
Since you code is not complete in your question, I assume that your SelectedLayout component might be having TouchableOpacity or something similar to handle tap (to select or remove image).
Basically what you want is to modify the datasource of your Flatlist (i.e this.state.imagesAddFile array) from SelectedLayout component.
Create a function in the component containing the flatlist that receives the image name (or image url depending on the structure of your image object) and that function should remove that image from your state (imagesAddFile). Now pass this function as a prop to your SelectedLayout and call this function from SelectedLayout component in onPress of your Touchable**** in SelectedLayout. You can use lodash methods are they are very handy and well written.(You'd be using them a lot)
Your component might look something like this:
handleImageTap = (imageName) => {
const { imagesAddFile } = this.state;
this.setState({
imagesAddFile: _.filter(imagesAddFile,imageObj =>
imageObj.name !== imageName);
})
}
render() {
return(
<FlatList
style={{ paddingBottom: 5, paddingTop: 10 }}
data={this.state.imagesAddFile}
keyExtractor={(y, z) => z.toString()}
renderItem={({ item }) => (
<SelectedLayout
ImageName = {item.name}
handleImageTap = {this.handleImageTap}
/>
)}
/>
)
The line
_.filter(imagesAddFile,imageObj => imageObj.name !== imageName);
is nothing but just a JSX shorthand inside lodash filter method, which is taking imagesAddFile array, iterating over it and returning new array by removing the image object which is having the name equal to image name. Please refer the doc for better clarification.
Ideally you should be checking first whether the image exist in the array or not. Also i'd suggest not to play with image name, use something unique like id, or imageUrl

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.

ReactNative - FlatList not updated until scroll

I have a problem with FlatList component which does not update until scrolled.
I tried add log to renderItem and keyExtractor both methods called with correct data but list didn't update.
Here is a render method:
render() {
const messages = this.props.messages
const message = this.props.message
return (
<View style={[styles.container]}>
<FlatList
ref={"flatList"}
contentContainerStyle={styles.list}
data={messages}
renderItem={(listItem) => {
return <MessageBuble message={listItem.item}/>
}}
keyExtractor={(item: Message) => {
return item.id
}}
/>
<View style={[styles.textInputContainer]}>
<TextInput
style={styles.textInput}
value={message}
multiline={true}
onChangeText={this.props.messageChanged}
/>
<Button title={"Odeslat"} onPress={() => {
if (this.props.sendMessage) {
this.props.sendMessage(this.props.message)
}
}}/>
</View>
</View>
)
}
Add extraData in FlatList and retry
<FlatList
extraData={this.props}
....
Tried the extraData, but that does not work.
There was an issue on Android where content was not visible when I returned back from another page to home screen (where the flatlist was present). The content was visible when I scrolled it a bit.
I assigned the main list to the extraData attribute, and could see that it changed in size via console logs. But the content remained invisible. Finally, used
onContentSizeChange={() => {
if (list.length > 0) {
ref.current.scrollToOffset({ animated: true, x: 0 });
}
}}
and it worked.