I am creating group by query in react native by using reduce function then I create section list to display data but I have some issues.
My code
<View>
<SectionList
renderSectionHeader={({ section: { title} }) => (
<Text style={{ fontWeight: 'bold' }}>{title}</Text>
)}
sections={this.state.dataSource}
renderItem={({item, index, section}) => <Text key={index}>{section.data}</Text>}
keyExtractor={(item, index) => item + index}
/>
</View>
const dataSource = responseJson.old_cases.reduce(function (sections, item) {
let section = sections.find(section => section.gender === item.gender);
if (!section) {
section = { gender: item.gender,data:[] };
sections.push(section);
}
section.data.push(item.name)
return sections;
}, []);
this.setState({dataSource: dataSource // Pass the dataSource that we've processed above});
but my output is like that
title //header
name_1,name2
name_1,name2
title2 //header
name_3
Output I want
title //header
name_1
name2
title2 //header
name_3
I just want to show one name per line against each title but according to my code render works fine because first title have two records so it render twice but both names are on same line twice
You're rendering all the data in your section at once, you need to render only one item at once.
In your flatList:
renderItem={({item, index, section}) => <Text key={index}>{item}</Text>}
Related
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>
I'm fetching data from an API and implementing search in a FlatList but the keyboard dismisses automatically after every key-press.
I'm refering this article but implementing it in a Functional Component.
const renderHeader = () => {
return <SearchBar
placeholder="Type Here..."
lightTheme
round
onChangeText={text => searchFilterFunction(text)}
value={value}
autoCorrect={false} />;
}
const searchFilterFunction = (text) => {
setValue(text);
const newData = APIData.filter(item => {
const itemData = `${item.name.toUpperCase()}`;
const textData = text.toUpperCase();
return itemData.includes(textData);
});
setData(newData);
}
return (
<FlatList
keyExtractor={(item) => item._id}
data={data}
ItemSeparatorComponent={renderSeparator}
ListHeaderComponent={renderHeader}
ListFooterComponent={renderFooter}
onRefresh={handleRefresh}
refreshing={refreshing}
renderItem={({ item }) => (
<Card>
<Card.Content style={{ flexDirection: "row" }}>
<Text>{"Name: " + item.name}</Text>
<Text>{"Status: " + (item.isaccepted ? "Accepted" : "Pending")}</Text>
<Text>{"ID: " + item.id}</Text>
</Card.Content>
</Card>
)} />
)
Thanks in advance.
I was doing same thing, adding search bar as a header to FlatList. Unfortunately, this also updates the header when you update the flatlist data when search filtering is complete and hence focusing out of SearchBar. At the end, due to time constraints, I ended up putting SearchBar at the top of FlatList.
Try rendering your ListHeaderComponent as JSX element directly, instead of using callback
<FlatList
ListHeaderComponent={
<View>
<Text>I am the header</Text>
</View>
}
...props
/>
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
I have a drawer with some text elements created dynamically.
I would like to select an item from the drawer, "show selected item" (like change the color of the text of that item) and re-change it to default when another one item is selected.
The text that I'd like to "change onPress" is inside the <TouchableWithoutFeedback> tag (i'm rendering some HTML code with react-native-render-html)
<FlatList
ItemSeparatorComponent={this.FlatListItemSeparator}
data={this.state.data}
renderItem={({ item }) => (
//on touch --> open article (call _onTextPress)
<TouchableWithoutFeedback onPress={this.navigateToScreen('Category', {id: item.id, title: item.name})}>
<View style={styles.categories}>
<HTML html={'<p style="color:#fd3a18; font-size:20px;"><strong>'+item.name+'</strong></p>\n'}/>
</View>
</TouchableWithoutFeedback>
)}
keyExtractor={({ id }, index) => id.toString()}
/>
navigateToScreen(routeName, params) {
return () => {
this.props.navigation.dispatch(NavigationActions.navigate({ routeName, params }))
this.props.navigation.closeDrawer();
};
}
If I understood correctly you want to change the color of selected item
//Add Selected Item to the State
state = {selectedItemId:'myId'}
// Change The State Whenever Selected
navigateToScreen(routeName, params) {
this.setState({selectedItemId:params.id})
return () => {
this.props.navigation.dispatch(NavigationActions.navigate({ routeName, params }))
this.props.navigation.closeDrawer();
};
}
Now Change the color conditionally.
<FlatList
ItemSeparatorComponent={this.FlatListItemSeparator}
data={this.state.data}
renderItem={({ item }) => (
//on touch --> open article (call _onTextPress)
<TouchableWithoutFeedback onPress={this.navigateToScreen('Category', {id: item.id, title: item.name})}>
<View style={styles.categories}>
<HTML html={`<p style="color:${this.state.selectedItemId == item.id ? "red" : "#fd3a18"}; font-size:20px;"><strong>'+item.name+'</strong></p>\n`}/>
</View>
</TouchableWithoutFeedback>
)}
keyExtractor={({ id }, index) => id.toString()}
/>
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.