Highlight a selected item in React-Native FlatList - react-native

I put together a simple React-native application to gets data from a remote service, loads it in a FlatList. When a user taps on an item, it should be highlighted and selection should be retained. I am sure such a trivial operation should not be difficult. I am not sure what I am missing.
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
FlatList,
ActivityIndicator,
Image,
TouchableOpacity,
} from 'react-native';
export default class BasicFlatList extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
data: [],
page: 1,
seed: 1,
error: null,
refreshing: false,
selectedItem:'null',
};
}
componentDidMount() {
this.makeRemoteRequest();
}
makeRemoteRequest = () => {
const {page, seed} = this.state;
const url = `https://randomuser.me/api/?seed=${seed}&page=${page}&results=20`;
this.setState({loading: true});
fetch(url)
.then(res => res.json())
.then(res => {
this.setState({
data: page === 1 ? res.results : [...this.state.data, ...res.results],
error: res.error || null,
loading: false,
refreshing: false
});
})
.catch(error => {
this.setState({error, loading: false});
});
};
onPressAction = (rowItem) => {
console.log('ListItem was selected');
console.dir(rowItem);
this.setState({
selectedItem: rowItem.id.value
});
}
renderRow = (item) => {
const isSelectedUser = this.state.selectedItem === item.id.value;
console.log(`Rendered item - ${item.id.value} for ${isSelectedUser}`);
const viewStyle = isSelectedUser ? styles.selectedButton : styles.normalButton;
return(
<TouchableOpacity style={viewStyle} onPress={() => this.onPressAction(item)} underlayColor='#dddddd'>
<View style={styles.listItemContainer}>
<View>
<Image source={{ uri: item.picture.large}} style={styles.photo} />
</View>
<View style={{flexDirection: 'column'}}>
<View style={{flexDirection: 'row', alignItems: 'flex-start',}}>
{isSelectedUser ?
<Text style={styles.selectedText}>{item.name.first} {item.name.last}</Text>
: <Text style={styles.text}>{item.name.first} {item.name.last}</Text>
}
</View>
<View style={{flexDirection: 'row', alignItems: 'flex-start',}}>
<Text style={styles.text}>{item.email}</Text>
</View>
</View>
</View>
</TouchableOpacity>
);
}
render() {
return(
<FlatList style={styles.container}
data={this.state.data}
renderItem={({ item }) => (
this.renderRow(item)
)}
/>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 50,
},
selectedButton: {
backgroundColor: 'lightgray',
},
normalButton: {
backgroundColor: 'white',
},
listItemContainer: {
flex: 1,
padding: 12,
flexDirection: 'row',
alignItems: 'flex-start',
},
text: {
marginLeft: 12,
fontSize: 16,
},
selectedText: {
marginLeft: 12,
fontSize: 20,
},
photo: {
height: 40,
width: 40,
borderRadius: 20,
},
});
When user taps on an item in the list, "onPress" method is invoked with the information on selected item. But the next step of highlight item in Flatlist does not happen. 'UnderlayColor' is of no help either.
Any help/advice will be much appreciated.

You can do something like:
For the renderItem, use something like a TouchableOpacity with an onPress event passing the index or id of the renderedItem;
Function to add the selected item to a state:
handleSelection = (id) => {
var selectedId = this.state.selectedId
if(selectedId === id)
this.setState({selectedItem: null})
else
this.setState({selectedItem: id})
}
handleSelectionMultiple = (id) => {
var selectedIds = [...this.state.selectedIds] // clone state
if(selectedIds.includes(id))
selectedIds = selectedIds.filter(_id => _id !== id)
else
selectedIds.push(id)
this.setState({selectedIds})
}
FlatList:
<FlatList
data={data}
extraData={
this.state.selectedId // for single item
this.state.selectedIds // for multiple items
}
renderItem={(item) =>
<TouchableOpacity
// for single item
onPress={() => this.handleSelection(item.id)}
style={item.id === this.state.selectedId ? styles.selected : null}
// for multiple items
onPress={() => this.handleSelectionMultiple(item.id)}
style={this.state.selectedIds.includes(item.id) ? styles.selected : null}
>
<Text>{item.name}</Text>
</TouchableOpacity>
}
/>
Make a style for the selected item and that's it!

In place of this.state.selectedItem and setting with/checking for a rowItem.id.value, I would recommend using a Map object with key:value pairs as shown in the RN FlatList docs example: https://facebook.github.io/react-native/docs/flatlist.html. Take a look at the js Map docs as well: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map.
The extraData prop recommended by #j.I-V will ensure re-rendering occurs when this.state.selected changes on selection.
Your onPressAction will obviously change a bit from example below depending on if you want to limit the number of selections at any given time or not allow user to toggle selection, etc.
Additionally, though not necessary by any means, I like to use another class or pure component for the renderItem component; ends up looking something like the following:
export default class BasicFlatList extends Component {
state = {
otherStateStuff: ...,
selected: (new Map(): Map<string, boolean>) //iterable object with string:boolean key:value pairs
}
onPressAction = (key: string) => {
this.setState((state) => {
//create new Map object, maintaining state immutability
const selected = new Map(state.selected);
//remove key if selected, add key if not selected
this.state.selected.has(key) ? selected.delete(key) : selected.set(key, !selected.get(key));
return {selected};
});
}
renderRow = (item) => {
return (
<RowItem
{...otherProps}
item={item}
onPressItem={this.onPressAction}
selected={!!this.state.selected.get(item.key)} />
);
}
render() {
return(
<FlatList style={styles.container}
data={this.state.data}
renderItem={({ item }) => (
this.renderRow(item)
)}
extraData={this.state}
/>
);
}
}
class RowItem extends Component {
render(){
//render styles and components conditionally using this.props.selected ? _ : _
return (
<TouchableOpacity onPress={this.props.onPressItem}>
...
</TouchableOpacity>
)
}
}

You should pass an extraData prop to your FlatList so that it will rerender your items based on your selection
Here :
<FlatList style={styles.container}
data={this.state.data}
extraData={this.state.selectedItem}
renderItem={({ item }) => (
this.renderRow(item)
)}
/>
Source : https://facebook.github.io/react-native/docs/flatlist
Make sure that everything your renderItem function depends on is passed as a prop (e.g. extraData) that is not === after updates, otherwise your UI may not update on changes

First
constructor() {
super();
this.state = {
selectedIds:[]
};
}
Second
handleSelectionMultiple = async (id) => {
var selectedIds = [...this.state.selectedIds] // clone state
if(selectedIds.includes(id))
selectedIds = selectedIds.filter(_id => _id !== id)
else
selectedIds.push(id)
await this.setState({selectedIds})
}
Third
<CheckBox
checked={this.state.selectedIds.includes(item.expense_detail_id) ? true : false}
onPress={()=>this.handleSelectionMultiple(item.expense_detail_id)}
/>
Finally i got the solution to my problem from the answer given by Maicon Gilton

Related

React Native Pass Index to Props

I have a modal that contain icons and description and status, and i want to pass the icons and descriptions from index to the modal,I already pass the status. is there anyway to do that? sorry i'm still new to react native and thanks in advance
this is my index.js
export const img =
{
itemStatus: {
"Open": { name: 'open-book', type: 'entypo', color: '#ffb732', desc:'New Attribut, New Attention'},
"Approved": { name: 'checklist', type: 'octicon', color: '#3CB371', desc:'Approved by SPV/MNG' },
"Escalated": { name: 'mail-forward', type: 'font-awesome', color: '#ffb732', desc:'Escalated to SPV/MNG' },
"Deliver Partial": { name: 'arrange-send-to-back', type: 'material-community', color: '#8B4513', desc:'Some items in a DO have not arrived/was faulty' },
};
and this is my container
class MyRequest extends React.Component {
constructor() {
super();
this.state = {
currentStatus: null,
refreshing: false,
fetchStatus: null
};
handleShowModal = (status) =>{
this.setState({
currentStatus: status,
});
}
handleDismissModal = () =>{
this.setState({currentStatus: null});
}
<View style={[styles.panelContainer, status === 'success' ? {} : { backgroundColor: color.white }]}>
<FlatList
showsVerticalScrollIndicator={false}
progressViewOffset={-10}
refreshing={this.state.refreshing}
onRefresh={this.onRefresh.bind(this)}
onMomentumScrollEnd={(event) => event.nativeEvent.contentOffset.y === 0 ? this.onRefresh() : null}
data={content}
renderItem={({ item }) => item}
keyExtractor={(item, key) => key.toString()}
/>
</View>
<IconModal visible={this.state.modalVisible} close={this.handleDismissModal} icon={} status={this.state.currentStatus} desc={} />
}
and this is my modal
const IconModal = (props) => {
return(
<Modal
isVisible={props.visible}
onBackdropPress={props.close}
>
<View style={styles.dialogBox}>
<View style={styles.icon}>
<Icon>{props.icon}</Icon>
</View>
<View style={styles.text}>
<Text style={styles.status}>{props.status}</Text>
<Text>{props.desc}</Text>
</View>
<TouchableOpacity onPress={props.close}>
<View>
<Text style={styles.buttonText}>GOT IT</Text>
</View>
</TouchableOpacity>
</View>
</Modal>
)
}
It's a bit unclear how you plan on mapping against img.itemStatus index but you can just reference the object you want as such.
import img from '....path_to_index.js'
...
// const currentItemStatus = img.itemStatus.Open
// OR
const itemStatus = 'Open' // Or 'Approved', 'Escalated', 'Deliver Partial'
const currentItemStatus = img.itemStatus[itemStatus]
...
<IconModal
visible={this.state.modalVisible}
close={this.handleDismissModal}
icon={currentItemStatus.name} // Passing name
status={this.state.currentStatus}
desc={currentItemStatus.desc} // Passing desc
/>
...
Hope this was helpful

Expo SDK 29 FlatList onRefresh not calling

Using Expo SDK 29 for a react native application.
I would like to use a flat list component. This makes up the entirety of a SafeAreaView component. I make this point as there are lots of issues relating to a flat list inside of a scroll view which this is not.
The flat list shows a list of jobs.
I have added a jobLoading boolean to the redux state to manage when the list should show as refreshing and can confirm that this toggles as expected when firing the actions to fetch the data and the success.
When i add the props to the flat list for onRefresh and refreshing the component seems to work by showing the activity indicator in the UI but does not fire the onRefresh function. I have tried implementing the call in numerous ways but nothing happens. The result is that the activity indicator shows itself and never disappears.
As it's Expo SDK 29 the React Native version is 0.55.4
Anyone have any ideas of what to try. I've spent a couple of hours looking at this trying various things but suggestions are welcome.
Thanks in advance.
EDIT: Added the code for reference. Reducer for refreshing sets true when fetchJobs() is dispatched and false when a success or error is recieved. The console log for onRefresh never triggers.
import * as React from 'react'
import * as actions from '../../redux/actions'
import { ActivityIndicator, FlatList, KeyboardAvoidingView, Dimensions, SafeAreaView, StyleSheet, View } from 'react-native'
import { ApplicationState, JobState, Job } from '../../redux'
import { Button, Form, Input, Item, Text, Icon } from 'native-base'
import { JobListItem } from './jobListItem'
import { StateHandlerMap, compose, lifecycle, withPropsOnChange, withStateHandlers } from 'recompose'
import { connect } from 'react-redux'
interface ReduxStateProps {
jobs: JobState
refreshing: boolean
screenOrientation: string
}
interface ReduxDispatchProps {
fetchJobs: (param?: string) => any
}
export interface DataItem {
key: string
data: Job
}
interface ListProps {
jobList: DataItem[]
}
interface SearchStateProps {
timer: number | undefined
searchString: string
}
interface SearchHandlerProps extends StateHandlerMap<SearchStateProps> {
updateSearch: (searchString: string) => any
setTimer: (timer: number | undefined) => any
}
type OuterProps = {}
type InnerProps = OuterProps & ReduxStateProps & ReduxDispatchProps & ListProps & SearchStateProps & SearchHandlerProps
const enhance = compose<InnerProps, OuterProps>(
connect<ReduxStateProps, ReduxDispatchProps, OuterProps, ApplicationState>(
state => ({
jobs: state.job,
refreshing: state.jobLoading,
screenOrientation: state.screenOrientation
}),
dispatch => ({
fetchJobs: (param?: string) => dispatch(actions.jobs.request({ param }))
})
),
withPropsOnChange<ListProps, OuterProps & ReduxStateProps & ReduxDispatchProps>(
['jobs', 'screenOrientation'],
props => ({
jobList: props.jobs && Object.keys(props.jobs).map(job => ({ key: job, data: props.jobs[Number(job)] }))
})
),
withStateHandlers<SearchStateProps, SearchHandlerProps, OuterProps>(
{
timer: undefined,
searchString: ''
},
{
updateSearch: state => (searchString: string) => ({ searchString }),
setTimer: state => (timer: number | undefined) => ({ timer })
}
),
lifecycle<InnerProps, {}>({
componentDidMount() {
this.props.fetchJobs()
}
})
)
export const JobList = enhance(({ fetchJobs, jobList, refreshing, screenOrientation, searchString, setTimer, timer, updateSearch }) => {
const onSearchChange = (search: string) => {
clearTimeout(timer)
updateSearch(search)
const timing = setTimeout(() => {
fetchJobs(search)
}, 500)
setTimer(timing)
}
const onRefresh = () => {
console.log('requesting refresh')
fetchJobs()
}
return (
<SafeAreaView style={{ flex: 1}}>
<KeyboardAvoidingView style={{ flexDirection: 'row', justifyContent: 'space-evenly', paddingTop: 3, paddingRight: 3 }}>
<Form style={{ flex: 1, paddingLeft: 10, paddingRight: 10 }}>
<Item>
<Input
value={searchString}
onChangeText={(text: string) => onSearchChange(text)}
placeholder='Search'
/>
</Item>
</Form>
<Button onPress={() => {fetchJobs(); updateSearch('')}}>
<Icon name='refresh' />
</Button>
</KeyboardAvoidingView>
{refreshing &&
<View style={styles.refreshContainer}>
<Text style={{ paddingBottom: 10 }}>Fetching Data</Text>
<ActivityIndicator />
</View>
}
<FlatList
keyExtractor={item => item.key}
data={jobList}
renderItem={({ item }) =>
<JobListItem
screenOrientation={screenOrientation}
item={item}
/>
}
onRefresh={onRefresh}
refreshing={refreshing}
/>
</SafeAreaView>
)
})
const styles = StyleSheet.create({
refreshContainer: {
height: 60,
flex: 1,
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center'
}
})
I'm having the exact same issue and I'm using expo SDK 30. But my case is a little bit different. The onRefresh function is called everytime I pull, however if I scroll down my list, and scroll back up fast, the loading indicator shows up, but my onRefresh function is not called.
My refreshing prop is set on my reducer, and my onRefresh function dispatches an action that fetches data and set refreshing true and false.
Here is my code:
class NoticiasScreen extends Component {
static navigationOptions = {
header: <Header
title='Notícias Alego'
leftComponent={<Image source={require('../../../assets/images/play_grande.png')} style={imageStyle} resizeMode='contain'/>}
/>
}
constructor(props) {
super(props);
this.renderItem = this.renderItem.bind(this);
this.keyExtractor = this.keyExtractor.bind(this);
this.renderContent = this.renderContent.bind(this);
this.navigateToNoticias = this.navigateToNoticias.bind(this);
this.carregarMaisNoticias = this.carregarMaisNoticias.bind(this);
this.onRefresh = this.onRefresh.bind(this);
}
componentDidMount() {
this.props.carregarNoticias(this.props.pagina);
}
renderItem({item}) {
return (
<NoticiaListItem noticia={item} abrirNoticia={this.navigateToNoticias} />
);
}
keyExtractor(item) {
return item.id.toString();
}
navigateToNoticias(noticia) {
this.props.navigation.navigate('NoticiasExibir', { id: noticia.id });
}
onRefresh() {
console.log('onRfresh');
this.props.carregarNoticias(1, true);
}
carregarMaisNoticias() {
const { carregarNoticias, pagina } = this.props;
carregarNoticias(pagina + 1);
}
renderContent() {
const { noticias, carregandoNoticias, erroNoticias } = this.props;
if(noticias.length === 0 && carregandoNoticias) {
return (
<View style={styles.containerCenter}>
<ActivityIndicator size="large" color={colors.verde}/>
</View>
);
}
if(erroNoticias) {
return (
<View style={styles.containerCenter}>
<Text style={styles.message}>{erroNoticias}</Text>
<TouchableOpacity hitSlop={hitSlop15}>
<Text>Recarregar</Text>
</TouchableOpacity>
</View>
)
}
return (
[<TextInput
style={styles.textInput}
placeholder='Pesquise'
key='pesquisa'
underlineColorAndroid='transparent'
/>,
<FlatList
data={noticias}
renderItem={this.renderItem}
keyExtractor={this.keyExtractor}
style={styles.list}
key='lista'
onRefresh={this.onRefresh}
refreshing={carregandoNoticias}
onEndReached={this.carregarMaisNoticias}
onEndReachedThreshold={0.1}
/>]
)
}
render() {
return (
<SafeAreaView style={styles.safeArea}>
<View style={styles.container}>
{this.renderContent()}
</View>
</SafeAreaView>
);
}
}
function mapStateToProps(state) {
return {
noticias: state.intranet.noticias,
pagina: state.intranet.pagina,
erroNoticias: state.intranet.erroNoticias,
carregandoNoticias: state.intranet.carregandoNoticias
}
}
function mapDispatchToProps(dispatch) {
return {
carregarNoticias: (pagina, recarregar) => dispatch(ActionCreator.carregarNoticias(pagina, recarregar))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(NoticiasScreen);
No idea what's going on. Any help is appreciated.
EDIT:
I fixed it somehow. I added the onMomentScrollBegin prop to prevent my flatList from rendering twice on Render, and that fixed this issue.
here is what I added:
constructor(props) {
super(props);
...
this.onRefresh = this.onRefresh.bind(this);
this.onMomentumScrollBegin = this.onMomentumScrollBegin.bind(this);
this.onEndReachedCalledDuringMomentum = true; //PUT THIS HERE
}
onRefresh() {
this.props.carregarNoticias(1, true);
}
carregarMaisNoticias() {
if(!this.onEndReachedCalledDuringMomentum){
const { carregarNoticias, pagina } = this.props;
carregarNoticias(pagina + 1);
this.onEndReachedCalledDuringMomentum = true;
}
}
onMomentumScrollBegin() {
this.onEndReachedCalledDuringMomentum = false;
}
render() {
<OptimizedFlatList
data={noticias}
renderItem={this.renderItem}
keyExtractor={this.keyExtractor}
style={styles.list}
key='lista'
onRefresh={this.onRefresh}
refreshing={carregandoNoticias}
onMomentumScrollBegin={this.onMomentumScrollBegin} //PUT THIS HERE
onEndReached={this.carregarMaisNoticias}
onEndReachedThreshold={0.1}
/>
}

React Native FlatList won't render

I'm trying to implement a FlatList in my React Native application, but no matter what I try the darn thing just won't render!
It's intended to be a horizontal list of images that a user can swipe through, but at this point I'd be happy to just get a list of text going.
Here's the relevant code. I can confirm that every render function is called.
render() {
const cameraScreenContent = this.state.hasAllPermissions;
const view = this.state.showGallery ? this.renderGallery() : this.renderCamera();
return (<View style={styles.container}>{view}</View>);
}
renderGallery() { //onPress={() => this.setState({showGallery: !this.state.showGallery})}
return (
<GalleryView style={styles.overlaycontainer} onPress={this.toggleView.bind(this)}>
</GalleryView>
);
}
render() {
console.log("LOG: GalleryView render function called");
console.log("FlatList data: " + this.state.testData.toString());
return(
<FlatList
data={this.state.testData}
keyExtractor={this._keyExtractor}
style={styles.container}
renderItem={
({item, index}) => {
this._renderImageView(item, index);
}
}
/>
);
}
}
_keyExtractor = (item, index) => index;
_renderImageView = (item, index) => {
console.log("LOG: renderItem: " + item);
return(
<Text style={{borderColor: "red", fontSize: 30, justifyContent: 'center', borderWidth: 5, flex: 1}}>{item}</Text>
);
}
//testData: ["item1", "item2", "item3", "item4", "item5", "item6", "item7", "item8", "item9"]
I'm fairly confident this isn't some flex issue, but in case I missed something here's the relevant stylesheets too:
const styles = StyleSheet.create({
container: {
flex: 1
},
overlaycontainer: {
flex: 1,
flexDirection: 'row',
justifyContent: 'space-between'
},
});
So, what I'm expecting to happen is to see a list of text items.
What's happening instead is I see a white screen with nothing on it.
Why is this list not rendering?
You can try replicating this if you want, define FlatList inside your return().
<View>
<FlatList
data={Items}
renderItem={this.renderItems}
enableEmptySections
keyExtractor={item => item.id}
ItemSeparatorComponent={this.renderSeparator}
/>
</View>
Then declare your Items inside constant(array of objects) outside your class like this,
const Items = [
{
id: FIRST,
title: 'item1',
},
{
id: SECOND,
title: 'item2',
},
{
id: THIRD,
title: 'item3',
},
// and so on......
];
After this get your items outside render by calling a function
onSelection = (item) => {
console.log(item.title)
}
renderItems = ({ item }) => {
return(
<TouchableOpacity
onPress={() => this.onSelection(item)}>
<View>
<Text>
{item.title}
</Text>
</View>
</TouchableOpacity>
)
}
Are you not missing the return on your renderItem?
render() {
console.log("LOG: GalleryView render function called");
console.log("FlatList data: " + this.state.testData.toString());
return(
<FlatList
data={this.state.testData}
keyExtractor={this._keyExtractor}
style={styles.container}
renderItem={
({item, index}) => {
return this._renderImageView(item, index);
}
}
/>
);
}
}

React Native:How to change dynamic view borderColor colour?

I have created 5 Text programatically in react native .
I want to change the borderColor colour of view By click item.
I tried using below code .But its changing all 5 views borderColor colour .
I want change borderColor colour of only one view.
for (var i = 0; i < 4; i++) {
pills.push (this.renderPill (i));
}
renderPill (index) {
return (
<TouchableOpacity key={index} style={ this.state.status ? boxStyle : boxStyleSelected } onPress={this.itemClick.bind(this)}>
<View >
<Text>
{index}
</Text>
</View>
</TouchableOpacity>
);
}
}
multiItemClick (index) {
this.setState({ status: true });
}
boxStyle: {
borderColor: '#ffffff',
},
boxStyleSelected: {
borderColor: '#000000',
}
You should use FlatList to render your item or component list.
Here are an Example:
render() {
return (
<View style={styles.container}>
<FlatList
data={ this.state.listItem }
ItemSeparatorComponent = {this._itemSeparator}
keyExtractor={(item, index) => index}
renderItem={this._renderItem}
selected={this.state.selected}
/>
</View>
);}
Separate the item list
_itemSeparator = () => (<View style={{ height: 1, backgroundColor: "#607D8B" }}/>);
Render the Item list
_renderItem = (item) => {
return (
<TouchableOpacity
key={item.index}
style={this.state.selected == item.index ? styles.boxSelected : styles.boxStyle}
onPress={()=>this._itemClick(item)}
>
<View style={{alignItems:'center'}}>
<Text>
{item.item.key}
</Text>
</View>
</TouchableOpacity>
);}
Event on Click item to Change the Style
_itemClick(item) { this.setState({selected:item.index}) }
Data State
constructor(props){
super(props);
this.state = {
selected: null,
listItem: [
{key: 'One'},
{key: 'Two'},
{key: 'Three'},
{key: 'Four'},
{key: 'Five'},
{key: 'Six'},
{key: 'Seven'},
{key: 'Eight'},
{key: 'Nine'},
{key: 'Ten'},
{key: 'Eleven'},
{key: 'Twelve'}
]
};}
Style Sheet your item
const styles = StyleSheet.create({
container :{
justifyContent: 'center',
flex:1,
margin: 10,
paddingTop:50
},
boxStyle: {
backgroundColor: '#778788',
padding: 10,
margin:3
},
boxSelected: {
backgroundColor: 'red',
padding: 10,
margin:3
}
});
enter image description here
This is what i got for you, Hope it will save your time.
The problem is, that you are using the state for all views. Try to modify the code to something, that depends on the index you clicked.
for (var i = 0; i < 4; i++) {
pills.push (this.renderPill (i));
}
renderPill (index) {
return (
<TouchableOpacity key={index} style={ this.state.status[index] ? boxStyle : boxStyleSelected } onPress={(index) => this.itemClick(index)}>
<View >
<Text>
{index}
</Text>
</View>
</TouchableOpacity>
);
itemClick(index) {
let status = this.state;
status[index] = true;
this.setState({
status: status
})
I have not tested this, so I am not 100% sure about the syntax, but i think the idea is clear. You have to save wich item is clicked in an array, so you know what item has to be rendered with the different border.
But I also would really advice to use a FlatList here https://facebook.github.io/react-native/docs/flatlist.html Because what you are trying to do is render a clickable List. That will give you way more control over the items, their direct styling and the list container. Also you would not need to have two separate lists, but could use an array of objects like this
{
value: 1
clicked: true
}
which makes it more readable and maintainable.
edit: there is actually a code sample for a multiselect on the link provided, i adapted it to your pills. Just hand the array to the component from somewhere else. Id and title is needed. View looks like this:
Code goes like this:
export default class MyView extends React.Component {
constructor(props) {
super(props);
this.state = {
pills: [{id: 1, title: 'Pill 1'},
{id: 2, title: 'Pill 2'},
{id: 3, title: 'Pill 3'}],
};
}
render() {
const {pills} = this.state;
return (
<MultiSelectList data={pills}/>
);
}
}
class MultiSelectList extends React.PureComponent {
state = {selected: (new Map())};
_keyExtractor = (item, index) => item.id;
_onPressItem = (id) => {
// updater functions are preferred for transactional updates
this.setState((state) => {
// copy the map rather than modifying state.
const selected = new Map(state.selected);
selected.set(id, !selected.get(id)); // toggle
return {selected};
});
};
_renderItem = ({item}) => (
<MyListItem
id={item.id}
onPressItem={this._onPressItem}
selected={!!this.state.selected.get(item.id)}
title={item.title}
/>);
render() {
return (
<FlatList
data={this.props.data}
extraData={this.state}
keyExtractor={this._keyExtractor}
renderItem={this._renderItem}
/>
);
}
}
class MyListItem extends React.PureComponent {
_onPress = () => {
this.props.onPressItem(this.props.id);
};
render() {
const color = this.props.selected ? "red" : "black";
return (
<TouchableOpacity onPress={this._onPress}>
<View>
<Text style={{borderStyle: 'solid', borderWidth: 2, borderColor: color }}>
{this.props.title}
</Text>
</View>
</TouchableOpacity>
);
}
}

Access section data from section items in react native SectionList

I need to access the information regarding the section (index, value) inside the renderItem in react-native SectionList. According to http://docs.w3cub.com/react_native/sectionlist/#renderitem section details can be passed via renderItem function. But in below code except the item all other values will be set to undefined. Is there any other possible way of doing it?
render(){
return(
<SectionList
sections={this.props.itemList}
renderItem={(item,section) => this._renderNewItem(item,section)}
renderSectionHeader={this._renderSectionHeader}
keyExtractor={(item) => item.id}
/>
)
}
_renderNewItem(item,section){
console.log(item, " ", section)
}
Sample data structure
renderItem prop passes a single parameter to the function. This parameter is an object includes item and section data.
renderItem: (info: { item: Item, index: number, section: SectionT,
separators: { highlight: () => void, unhighlight: () => void,
updateProps: (select: 'leading' | 'trailing', newProps: Object) =>
void, }, }) => ?React.Element
To get the section data you can use it like below
renderItem={({ item, section }) => this._renderNewItem(item,section)}
Update
Adding a sample example to demonstrate how it works. See it on snack.expo.io
import React, { Component } from 'react';
import { Text, View, StyleSheet, SectionList } from 'react-native';
import { Constants } from 'expo';
const data = [{key: 'New', data: [{name: 'Foo1'}, {name: 'Foo2'}]}, {key: 'Old', data: [{name:'Foo3'}, {name: 'Foo4'}]}];
export default class App extends Component {
_renderItem = ({item, section}) => (<Text>{`${item.name}(${section.key})`}</Text>)
_renderSectionHeader = ({section}) => {
return (
<View style={styles.sectionHeader}>
<Text style={styles.header}>{section.key}</Text>
</View>
)
}
render() {
return (
<View style={styles.container}>
<SectionList
sections={data}
renderItem={this._renderItem}
renderSectionHeader={this._renderSectionHeader}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
},
sectionHeader: {
height: 50,
flex: 1,
backgroundColor: '#fff',
justifyContent: 'center',
paddingLeft: 10
},
header: {
fontSize: 20,
}
});
Output of this will look like attached image Link
https://i.stack.imgur.com/Y7CLF.png Output of this solution image.
https://pastebin.com/embed_js/7kYrk8kf Dummy JSON File URL Link for this Demo.
I used above mentioned JSON with Section List in following way with the help of #bennygenel code.
renderItem = ({ item, section }) => <Text>{`${item.title}`}</Text>;
renderSectionHeader = ({ section }) => {
return (
/* <View style={styles.sectionHeader}>
<Text style={styles.header}>{section.key}</Text>
</View> */
****Custom Header Component ****
<CellHeader title={section.title} />
);
};
renderSectionList = () => {
return (
<View>
<SectionList
sections={data1.results}
renderItem={this.renderItem}
renderSectionHeader={this.renderSectionHeader}
/>
</View>
);
};