'React Native Table with Row Selection
I am trying to create a React Native screen that allows the user to select which items to send to the server for batch processing.
My thought was to have a table, and allow the user to select the rows they want, then click a button to submit to the server.
I need the state to contain a list of the ids from those rows, so that I can use it to allow the user to send a request with that array of ids.
A mock-up of my code is below, but it doesn't work. When the update of state is in place, I get an error of "selected items is not an object". When it is commented out, of course the state update doesn't work, but it also doesn't set the value of the checkbox from the array if I hard code it in the state initialization (meaning is 70 is in the array, the box is still not checked by default), and it does allow the box to get checked but not unchecked. How do I get it working?
import React, { Component } from 'react';
import { StyleSheet, View } from 'react-native';
import CheckBox from '@react-native-community/checkbox';
import { Table, Row, TableWrapper, Cell } from 'react-native-table-component';
import moment from 'moment';
class FruitGrid extends Component {
constructor(props) {
super(props);
}
state = {
selectedItems : [70],
data: []
};
refresh() {
let rows = [
[69,'David','Apples'],
[70,'Teddy','Oranges'],
[73,'John','Pears']
];
this.setState({data: rows});
}
componentDidMount() {
this.refresh();
}
setSelection(id) {
const { selectedItems } = this.state;
if (id in selectedItems)
{
this.setState({selectedItems: selectedItems.filter(i => i != id)});
}
else
{
this.setState({selectedItems : selectedItems.push(id)});
}
}
render() {
const { selectedItems, data } = this.state;
let columns = ['',
'Person',
'Fruit'];
return (
<View style={{ flex: 1 }}>
<Table borderStyle={{borderWidth: 2, borderColor: '#c8e1ff'}}>
<Row data = {columns} />
{
data.map((rowData, index) =>
(
<TableWrapper key={index} style={styles.row}>
<Cell key={0} data = {<CheckBox value={rowData[0] in selectedItems} onValueChange={this.setSelection(rowData[0])} />} />
<Cell key={1} data = {rowData[1]} textStyle={styles.text}/>
<Cell key={2} data = {rowData[2]} textStyle={styles.text}/>
</TableWrapper>
)
)
}
</Table>
</View>
);
}
}
export default FruitGrid;
const styles = StyleSheet.create({
btn: { width: 58, height: 18, backgroundColor: '#8bbaf2', borderRadius: 2 },
btnText: { textAlign: 'center', color: '#000000' },
text: { margin: 6 },
row: { flexDirection: 'row' },
});
Solution 1:[1]
Thanks to a friend, I found 3 issues in the code that were causing the problem:
When checking to see if the array contains the object, I first need to check that the array is an array and contains items. New check (wrapped in a function for reuse):
checkIfChecked(id, selectedItems) { return selectedItems?.length && selectedItems.includes(id); }
The state update was modifying the state without copying. New state update function:
setSelection(id) { const { selectedItems } = this.state;
if (this.checkIfChecked(id,selectedItems)) { this.setState({selectedItems: selectedItems.filter(i => i != id)}); } else { let selectedItemsCopy = [...selectedItems] selectedItemsCopy.push(id) this.setState({selectedItems : selectedItemsCopy}); }}
The onValueChange needed ()=> to prevent immediate triggering, which lead to a "Maximum Depth Reached" error. New version
onValueChange={()=>this.setSelection(rowData[0])} />}
The full working code is here:
import React, { Component } from 'react';
import { StyleSheet, View } from 'react-native';
import CheckBox from '@react-native-community/checkbox';
import { Table, Row, TableWrapper, Cell } from 'react-native-table-component';
import moment from 'moment';
class FruitGrid extends Component {
constructor(props) {
super(props);
}
state = {
selectedItems : [],
data: []
};
refresh() {
let rows = [
[69,'David','Apples'],
[70,'Teddy','Oranges'],
[73,'John','Pears']
];
this.setState({data: rows});
}
componentDidMount() {
this.refresh();
}
setSelection(id) {
const { selectedItems } = this.state;
if (this.checkIfChecked(id,selectedItems))
{
this.setState({selectedItems: selectedItems.filter(i => i != id)});
}
else
{
let selectedItemsCopy = [...selectedItems]
selectedItemsCopy.push(id)
this.setState({selectedItems : selectedItemsCopy});
}
}
checkIfChecked(id, selectedItems)
{
return selectedItems?.length && selectedItems.includes(id);
}
render() {
const { selectedItems, data } = this.state;
let columns = ['',
'Person',
'Fruit'];
return (
<View style={{ flex: 1 }}>
<Table borderStyle={{borderWidth: 2, borderColor: '#c8e1ff'}}>
<Row data = {columns} />
{
data.map((rowData, index) =>
(
<TableWrapper key={index} style={styles.row}>
<Cell key={0} data = {<CheckBox value={this.checkIfChecked(rowData[0],selectedItems)} onValueChange={()=>this.setSelection(rowData[0])} />} />
<Cell key={1} data = {rowData[1]} textStyle={styles.text}/>
<Cell key={2} data = {rowData[2]} textStyle={styles.text}/>
</TableWrapper>
)
)
}
</Table>
</View>
);
}
}
export default FruitGrid;
const styles = StyleSheet.create({
btn: { width: 58, height: 18, backgroundColor: '#8bbaf2', borderRadius: 2 },
btnText: { textAlign: 'center', color: '#000000' },
text: { margin: 6 },
row: { flexDirection: 'row' },
});
Solution 2:[2]
I introduced my own module for this feature from which you will be able to select row easily and much more.
You can use this component like this below
import DataTable, {COL_TYPES} from 'react-native-datatable-component';
const SomeCom = () => {
//You can pass COL_TYPES.CHECK_BOX Column's value in true/false, by default it will be false means checkBox will be uncheck!
const data = [
{ menu: 'Chicken Biryani', select: false }, //If user select this row then this whole object will return to you with select true in this case
{ menu: 'Chiken koofta', select: true },
{ menu: 'Chicken sharwma', select: false }
]
const nameOfCols = ['menu', 'select'];
return(
<DataTable
onRowSelect={(row) => {console.log('ROW => ',row)}}
data={data}
colNames={nameOfCols}
colSettings={[{name: 'select', type: COL_TYPES.CHECK_BOX}]}
/>
)
}
export default SomeCom;
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | David Schwartz |
| Solution 2 | Muhammad Rafeh Atique |



