React native how to render items without using any loder? - react-native

I have implemented a delete feature for the image editor app.
Case: If the user added a number of images in edit mode and wants to delete the unnecessary layer of the image from the editor then allows to delete the selected layer from the array.
Issue: when deleted the selected layer from the array the remaining image swaps its position and size to deleted image position and size. Even if I double-check the scale value and position remain the same as I fixed. This issue occurs if am not using the loder and if am using the loder then the image did not swap their position and scale.
Where I handled the states: I am handling the states in the component and I also checked if I handled the states in redux same error occurred. App speed to slow if handled with redux.
Here is my code.
const onDeleteLayerSequence = data => {
let indexing = data.length;
data.forEach(item => {
indexing--;
item.key = indexing;
});
setInputData(data);
setIsDelete(!isDelete);
};
const deleteLayer = index => {
const filterItem = [
...inputData.slice(0, index),
...inputData.slice(index + 1, inputData.length),
];
onDeleteLayerSequence(filterItem);
};
useEffect(() => {
setImageArray(inputData.filter(item => item.type == 'image'));
// setLoader(true);
}, [isDelete]);
return (
<>
<AutoDragSortableView
dataSource={inputData}
parentWidth={util.WP(100)}
childrenWidth={util.WP(100)}
childrenHeight={util.WP(12)}
keyExtractor={(item, index) => item.id}
renderItem={(item, index) => render_item(item, index)}
onDataChange={data => onSelectLayer(data)}
onClickItem={(data, i) => replaceImage(data, i)}
/>
</View>
</DraggablePanel>
<View style={styles.editImageWrapper}>
<ViewShot
ref={reference}
options={{format: 'jpg', quality: 1.0, result: 'base64'}}>
<ImageBackground
style={[
styles.templateContainer,
{backgroundColor: background},
]}
resizeMode="stretch"
source={selectTemplate}>
{isFocused &&
imageArray &&
imageArray.map((item, i) => {
return (
<component.PinchZoomView
minScale={0.1}
maxScale={5}
scale={item.scale}
translateX={item.translateX}
translateY={item.translateY}
data={value =>
modifyValues(value, item.layer, item.key)
}
key={i}
style={[
styles.pinchImagePosition,
{
zIndex: item.key,
},
]}>
<ImageCarousel>
<Image
source={{
uri: item.editImage,
height: util.WP(100),
width: util.WP(100),
}}
key={item}
resizeMode="contain"
/>
</ImageCarousel>
</component.PinchZoomView>
);
})}
</ImageBackground>
</ViewShot>
</View>
</>
)

Related

How can I change the numColumns of Flatlist when changing the orientation

I created the function name getOrientation() and put it at useEffect so that whenever I rotate the device, it will rerender the component and show me which orientation the device is.
I also created the variable to determine the orientation by using hook
getOrientation()
const [orientation, setOrientation] = useState("")
const window = useWindowDimensions()
const getOrientation = () => {
if (window.height < window.width) {
setOrientation("LANDSCAPE")
} else {
setOrientation("PORTRAIT")
}
return orientation
}
at useEffect
useEffect(() => {
getOrientation()
})
console.log(orientation)
My problem is that I want to set the numsColumns = 2 in Flatlist (LANDSCAPE) and equals to 1 for the portrait mode but the error popup telling me that I cannot change the numColumns on the fly. What should I do ?
Here is my flatlist
<View style={styles.container}>
<FlatList
contentContainerStyle={{
paddingLeft: insets.left,
paddingRight: insets.right,
}}
data={dishes.dishes}
numColumns={orientation == "LANDSCAPE" ? 2 : 1}
renderItem={({ item, index }) => (
<Tile
style={styles.tileItem}
key={index}
title={item.name}
caption={item.description}
onPress={() => navigation.navigate('Dishdetail_Screen', { dishId: item.id })} // passing infor from one to another screen
// chevron={false}
featured
imageSrc={{
uri: baseUrl + item.image
}}
/>
)}
keyExtractor={item => item.id.toString()} />
</View>
This creepy error
enter image description here
P/s: I'm the fresh React-Native developer. Thanks everyone who is checking my problems
Try adding a key prop to your Flatlist like so with the value being ur orientation:
<FlatList
key={orientation} // add key prop here
contentContainerStyle={{
paddingLeft: insets.left,
paddingRight: insets.right,
}}
data={dishes.dishes}
numColumns={orientation == "LANDSCAPE" ? 2 : 1}
renderItem={({ item, index }) => (
<Tile
style={styles.tileItem}
key={index}
title={item.name}
caption={item.description}
onPress={() => navigation.navigate('Dishdetail_Screen', { dishId: item.id })} // passing infor from one to another screen
// chevron={false}
featured
imageSrc={{
uri: baseUrl + item.image
}}
/>
)}
keyExtractor={item => item.id.toString()} />

FlatList not rendering style dynamically

I'm currently struggling in making my FlatList applying the changes I do to it. What I am wanting right now is that when I click an item in my flatlist, that it highlights in a certain color. I followed an approach done by a guy but I am having the problem that to me is not working the update once I click.
I can see through console that all I am doing performs a modification but I think that I am missing some point with extraData parameter since it is not re-rendering with the backgroundColor that I would like to apply.
The code I have is as following, I know that the style I am applying is correct since if i substitute in the map styles.list per styles.selected, everything gets the background I would like to be applied to the elements I click.
So summarizing, the issue I think I have is that the flatlist is not re-rendering so it doesn't show the modifications I perform on it. Any idea of what I am doing wrong? Any tip?
render() {
const { students, studentsDataSource, loading, userProfile } = this.props.navigation.state.params.store;
this.state.dataSource = studentsDataSource._dataBlob.s1.map(item => {
item.isSelect = false;
item.selectedClass = styles.list;
return item;
})
const itemNumber = this.state.dataSource.filter(item => item.isSelect).length;
return (
<View style={styles.container}>
<Item rounded style={styles.searchBar}>
<Input placeholder='Group Name'/>
</Item>
<FlatList
style={{
flex: 1,
width: "100%",
}}
data={this.state.dataSource}
ItemSeparatorComponent={this.FlatListItemSeparator}
renderItem={ ({ item }) => (
<ListItem avatar style={[styles.list, item.selectedClass]}
onPress={() => this.selectItem(item)}>
<Left>
{!item.voteCount && <Avatar unseen={true} /> }
{!!item.voteCount > 0 && <Avatar />}
</Left>
<Body>
<Text>{item.name}</Text>
<Text note>{item.group}</Text>
</Body>
</ListItem>
)
}
listKey={item => item.key}
extraData={this.state}
/>
</View>
);
}
Here we can find the state and SelectItem functions:
constructor(props) {
super(props)
this.state = {
dataSource : [],
}
}
//FlatListItemSeparator = () => <View style={styles.line} />;
selectItem = data => {
//{console.log("inside SelectItem=", data)}
data.isSelect = !data.isSelect;
data.selectedClass = data.isSelect? styles.selected: styles.list;
const index = this.state.dataSource.findIndex( item => data.key === item.key);
this.state.dataSource[index] = data;
this.setState({
dataSource: this.state.dataSource,
});
console.log("This state has the changes:=",this.state.dataSource)
};
Well the main issue was that I was not using the .setState and instead I was doing assignations which killed the listeners.

FlatList Weird Behavior after appending new data - multiple automatic scrolling

I have a list of data that shows posts feed, whenever user scrolls, the list automatically scrolls multiple times up or down which results in a very bad UX
tried the workaround provided here:
https://stackoverflow.com/questions/43611485/prevent-flatlist-from-scrolling-when-new-items-are-added#=
but it doesn't seem to work for me.
<FlatList
keyboardDismissMode = 'on-drag'
keyboardShouldPersistTaps = 'always'
getItemLayout={(data, index) => (
{length: 575, offset: 575 * index, index}
)}
onScroll = {(event) => {
this.flatListOffset = event.nativeEvent.contentOffset.y
this.setState({
scrollOffset: event.nativeEvent.contentOffset.y
})
}}
contentContainerStyle={{
flexGrow: 1,
width: Dimensions.get('window').width
}}
refreshControl={
<RefreshControl
progressViewOffset={120}
refreshing={this.state.refreshing}
onRefresh={this._onRefresh.bind(this)}
/>
}
ref={(feedView) => (this.feedView = feedView)}
data={this.state.feed}
onEndReached={() => {
if (this.state.hasNextPage && !this.state.loadingMore) {
this.paginateFeed()
}
}}
ListHeaderComponent={this._renderCirclesBar}
ListEmptyComponent={this._renderEmptyState}
ListFooterComponent={() =>
!this.state.loadingMore ? null : (
<ActivityIndicator color="#2962FF" size="large" />
)
}
keyExtractor={this._keyExtractor}
renderItem={this._renderItem}
/>
then at some point:
this.setState({ feed: this.state.feed.concat(....) })
a GIF explaining the issue:
https://gifyu.com/image/9gDD
User PureComponent for rendering and set prop disableVirtualization={true} in flatlist

How to add load more records with Spinner in FlatList react-native (means -10 - 10 records) manually! not from using server side

Hi I am developing sample application based on FlatList this is my code here. Actually i showed entire records like i have 50 records to my account . But now i am displaying entire 50 records. Bur i need show 10 after adding to 10 records. But i don't know adding to FlatList.
Here this is my code:
<FlatList
data={this.state.profiles}
renderItem={({ item, index }) => this.renderCard(item, index)}
keyExtractor={item => item.id}
ItemSeparatorComponent={() => <Divider style={{ marginTop: 5, marginLeft: width * 0.2 + 20 }} parentStyle={{ backgroundColor: globalStyles.BG_COLOR, alignItems: 'baseline' }} />}
/>
renderCard (profile, index) {
console.log('rendercard', profile);
//
return (
<View key={profile.id}>
<ProfileCard
profile={profile}
style={styles.card}
onPress={() => this.props.screenProps.rootNavigation.navigate('Profile', { profile: this.state.profile, id: profile.id })}
// onPress={() => alert('PROFILE')}
onAddClick={() => this.setState({ connectionPageVisible: true, cardProfile: profile })}
connectedIds={(this.props.screenProps && this.props.screenProps.connectedIds) || this.props.connectedIds}
/>
</View>
);
}
Please show me load more records with Activity Indicator.
Thanks in Advance
If I have understood your problem properly, then you are looking for infinite scrolling in Flatlist. You can achieve this with the help of onEndReached and onEndThreshold attributes.
Consider the following prototype
Assuming you are storing records into this.state.profiles.
Pulling new records from the server
Setting initial page number in the constructor
constructor(props){
super(props);
this.state = { page: 0}
}
Fetching new records
fetchRecords = (page) => {
// following API will changed based on your requirement
fetch(`${API}/${page}/...`)
.then(res => res.json())
.then(response => {
this.setState({
profiles: [...this.state.profiles, ...response.data] // assuming response.data is an array and holds new records
});
});
}
to handle scroll
onScrollHandler = () => {
this.setState({
page: this.state.page + 1
}, () => {
this.fetchRecords(this.state.page);
});
}
Render function
render() {
return(
...
<FlatList
data={this.state.profiles}
renderItem={({ item, index }) => this.renderCard(item, index)}
keyExtractor={item => item.id}
ItemSeparatorComponent={() => <Divider style={{ marginTop: 5, marginLeft: width * 0.2 + 20 }} parentStyle={{ backgroundColor: globalStyles.BG_COLOR, alignItems: 'baseline' }} />}
onEndReached={this.onScrollHandler}
onEndThreshold={0}
/>
...
);
}
Local updates
If you have already pulled all the data, but want to show only 10 at a time, then all you need to do is change the fetchRecords
fetchRecords = (page) => {
// assuming this.state.records hold all the records
const newRecords = []
for(var i = page * 10, il = i + 10; i < il && i < this.state.records.length; i++){
newRecords.push(this.state.records[i]);
}
this.setState({
profiles: [...this.state.profiles, ...newRecords]
});
}
Above approach will show Activity Indicator while pulling records.
Hope this will help!

React Native - SectionList numColumns support

FlatList has numColumns support. How to set numColumns with SectionList?
Github issue: SectionList renderItem multi item support #13192
Here is my solution to numColumns for SectionList. If you have better let me know please.
class Example extends Component {
static propTypes = {
numColumns: PropTypes.number
};
static defaultProps = {
numColumns: 2
};
_renderSection = data => <Section {...data} />;
_renderItem = ({ section, index }) => {
const { numColumns } = this.props;
if (index % numColumns !== 0) return null;
const items = [];
for (let i = index; i < index + numColumns; i++) {
if (i >= section.data.length) {
break;
}
items.push(<Item item={section.data[i]} />);
}
return (
<View
style={{
flexDirection: "row",
justifyContent: "space-between"
}}
>
{items}
</View>
);
};
render() {
return (
<SectionList
sections={dumyData}
style={styles.container}
renderItem={this._renderItem}
renderSectionHeader={this._renderSection}
/>
);
}
}
It is possible to use FlatList with numColumns prop as the renderItem of SectionList.
const data = [ //Notice [[...]] instead of [...] as in the RN docs
{data: [[...]], title: ...},
{data: [[...]], title: ...},
{data: [[...]], title: ...},
]
render () {
return (
<SectionList
renderItem={this._renderSectionListItem}
renderSectionHeader={this._renderSectionHeader}
sections={data}
/>
)
}
renderSectionListItem = ({item}) => {
return (
<FlatList
data={item}
numColumns={3}
renderItem={this.renderItem}
/>
)
}
Digging this issue up, I came with a solution similar to Pir Shukarullah Shah 's.
I'm using FlatList instead of my regular item, taking into account only the first item in <SectionList/>'s renderItem method.
_renderList = ({ section, index }) => {
if (index !== 0) return null;
return (
<FlatList numColumns={columns}
columnWrapperStyle={styles.container}
data={section.data}
renderItem={this._renderItem}
keyExtractor={keyExtractor}
/>
)
}
...
<SectionList
renderItem={this._renderList}
renderSectionHeader={this._renderSectionHeader}
sections={itemList}
keyExtractor={keyExtractor}
/>
I found there is a simple solution. Please try adding the following property to the
contentContainerStyle={{
flexDirection : 'row',
justifyContent : 'flex-start',
alignItems : 'flex-start',
flexWrap : 'wrap'
}}
Besides, set and render the Section Header with the Width equal to the SectionList width. Otherwise, the list items will be displayed following the Section Header in row direction.
const DATA = [
{
renderItem: ({ item, index }) => {
return (<View style={{flexDirection:'row', alignItems:'center', justifyContent:'space-between', }}>
{item.map((elem,index)=>(<View style={{ borderColor: 'black', borderWidth: 2, minWidth:100 }}>
<Text>{elem.value}</Text>
</View>))
}
</View>);
},
data: [
[{id:'1', value:'Pizza'}, {id:'2', value:'Burger'}, {id:'3', value:'Onion Rings'}], //this array length will be noOfColumns
[{id:'4', value:'Risotto'}, {id:'5', value:'French Fries'}, {id:'6', value:'Water'}],
],
},
<SectionList
ref={listRef}
sections={DATA}
keyExtractor={_keyExtractor}
/>
I had the same logic like Pir Shukarullah Shah. The idea of using flexWrap is not recommended by react and warns to use numColumns prop in flatlist. If anyone has a better solution please add.
let items = []
const renderItem = ({ item, index }) => {
if (index % 2 === 0) {
items = []
items.push(<Card cloth={item} index={index} />)
return (index === clothes[0].data.length - 1) ? <View style={styles.row}>{items}</View> : null
}
items.push(<Card cloth={item} index={index} />)
return (
<View style={styles.row}>
{items}
</View>
)
}
The section list is :
<SectionList
sections={clothes}
renderItem={renderItem}
keyExtractor={(item, index) => index}
renderSectionHeader={renderSectionHeader}
stickyHeaderHiddenOnScroll={true}
stickySectionHeadersEnabled={true}
onEndReached={endReachedHandler}
onEndReachedThreshold={0.25}
contentContainerStyle={{ paddingBottom: '25%' }}
/>
The structure for clothes is:
let one = {name: 'Jeans pant'}
let many = Array(10).fill(one) // creating more dummy clothes
let cl = [{data: many, title: 'Cloth'}]
let [clothes, setClothes] = useState(cl)
I needed only one section so in cl array I wrote only one object initially if you want to have multiple sections you would need to add to the clothes array.
This is a slightly updated version of Pir Shukarullah Shah accepted answer to show a more functional approach over class approach.
// render a single section.data item
const itemRenderer = (item) => <Text>{item}</Text>
return (
<SectionList
sections={listData}
renderItem={(section, index) => {
if (index % numCols) { // items are already consumed
return null
}
// grab all items for the row
const rowItems = section.data.slice(index, index+numCols)
// wrap selected items in a "row" View
return <View
style={{
flexDirection:"row",
justifiyContent:"space-between"
}}
>{rowItems.map(itemRenderer)}</View>
}}
/>)
Also if you have fixed width items you can calculate numCols dynamically here's an example for a full screen width SectionList:
const itemFixedWidth = 24
const listWidth = useWindowDimensions().width
const numCols = Math.floor(listWidth / itemFixedWidth)
I'm a new user to this site, otherwise I'd just upvote Fong's answer above. Slick, that one.
Just to further clarify the last sentence he wrote.
I used Dimensions.get('window').width on the section header like so:
renderSectionHeader={({ section: { title } }) => (
<View
style={{
width: Dimensions.get('window').width,
}}
>
<Text>
{title}
</Text>
</View>
)}
Though that method does throw a console warning about using flexWrap...