I am trying to make dynamic screen where i am adding multiple CustomSpinners
using forEach
loop in react-native.
The problem I am facing is that whenever I am selecting value from any spinner that value gets updated for all spinners, as the index remains the same. Suppose when I click on color spinner and choose Cyna which is at index 2 and when I type this.setState({name: index});
it calls Render method again and the whole view get draw again and as the itemName={this.state.name}
is set to 2 so it will auto select all the spinner values based on index 2 , what I want that when I tap on Color spinner and choose cyan color then only that Color spinner gets updated instead of updating all spinners at same index.
Can any one let me know how to achieve this? I am already scratching my head from past 2 days on this. Please let me know your suggestions that will really help me lot.
Here is my code :-
onClickColor(data) {
this.setState({ name: index });
}
renderSpinerData(item) {
return <CustomDynamicSpinner
title={"Chosse " + item.label}
listArray={item.options}
onClick={(data,index) => this.onClickDropdown(data,index)}
itemName={this.state.name}
btnStyle={{
height: 42,
marginBottom: 2,
borderBottomWidth: 1 / 2,
justifyContent: "center"
}}
txtColor={appColor.lightGrey}
style={{
height: 42,
marginBottom: 2,
borderBottomWidth: 1 / 2
}}
closeIconButtonStyle={styles.closeButtonStyle}
/>
};
renderConfigurableProductDetail() {
let array=[];
if (CustomConfigArray.length>0){
array = CustomConfigArray;
} else {
array = this.props.ProductDetailState.productData.configurable;
}
{
return array.map((item) => {
if(item.label!="Size"){
return (
<View style={{ flex: 1, backgroundColor: "white", flexDirection: "column", marginTop: 8 }}>
<CustomText style={{ fontSize: 16, fontFamily: "futuraLigtBt", marginLeft: 6, paddingLeft: 15, paddingRight: 15, paddingTop: 5, paddingBottom: 5 }}>
{item.label}
</CustomText>
{this.renderSpinerData(item)}
</View>;
)
}
})
}
};
My CustomSpiner class :-
class DynamicListViewModal extends Component {
constructor(props) {
super(props);
this.state = {
dataSource: ds.cloneWithRows(this.props.listArray),
listArray: this.props.listArray
};
this.handleClick = this.handleClick.bind(this);
this.renderRow = this.renderRow.bind(this);
this.renderList = this.renderList.bind(this);
}
handleClick(data, index) {
this.props.onClick(data, index);
this.props.onClose();
}
renderRow(rowData) {
const separatorStyle = this.props.separatorStyle;
const rowTextStyle = this.props.rowText;
const rowStyle = this.props.rowStyle;
const rowId = this.props.listArray.indexOf(rowData);
let separator = <View style={separatorStyle} />;
let row = (
<View style={rowStyle}>
<Text style={rowTextStyle}>{rowData.label}</Text>
</View>
);
if (this.props.renderRow) {
row = this.props.renderRow(rowData, rowId);
}
return (
<View>
<TouchableOpacity onPress={() => this.handleClick(rowData, rowId)}>
{row}
</TouchableOpacity>
{separator}
</View>
);
}
renderList() {
const listViewStyle = this.props.listViewStyle || DefaultStyles.listView;
return (
<ListView
style={listViewStyle}
dataSource={this.state.dataSource}
renderRow={(rowData) => this.renderRow(rowData)}
automaticallyAdjustContentInsets={false}
enableEmptySections={true}
/>
);
}
render() {
const containerStyle = this.props.containerStyle;
const topBarStyle = this.props.topBarStyle;
const iconContainerStyle = this.props.iconContainerStyle;
const closeIconButtonStyle = this.props.closeIconButtonStyle;
const titleStyle = this.props.titleStyle;
const title = this.props.title;
return <View style={containerStyle}>
<View style={topBarStyle}>
<View style={iconContainerStyle} />
<Text style={[titleStyle, { fontWeight: "bold", fontSize: 17 }]}>
{title}
</Text>
<TouchableOpacity style={iconContainerStyle} onPress={() => this.props.onClose()}>
<Image source={require("../../assets/cancel.png")} style={closeIconButtonStyle} />
</TouchableOpacity>
</View>
{this.renderList()}
</View>;
}
}
class CustomDynamicSpinner extends Component {
constructor(props) {
super(props);
this.state = {
data: this.props.data,
popoverIsOpen: false
};
this.onClick = this.onClick.bind(this);
}
onClick(data) {
console.log("selected data:", data);
}
render() {
const onClick = this.props.onClick || this.onClick;
return <View>
<TouchableOpacity style={this.props.btnStyle} onPress={() => this.setState(
{ popoverIsOpen: true }
)}>
<Text style={{ color: this.props.txtColor }}>
{(this.props.itemName!="value" || this.props.itemName == 0) ? this.props.listArray[this.props.itemName].label : "Please select"}
</Text>
</TouchableOpacity>
<Modal animationType={"slide"} transparent={false} visible={this.state.popoverIsOpen} onRequestClose={() => {
console.log("Modal has been closed.");
}}>
<View>
<DynamicListViewModal listArray={this.props.listArray} title={this.props.title} onClick={onClick} onClose={() => this.setState(
{ popoverIsOpen: false }
)} containerStyle={this.props.containerStyle} listViewStyle={this.props.listViewStyle} separatorStyle={this.props.separatorStyle} topBarStyle={this.props.topBarStyle} titleStyle={this.props.titleStyle} iconContainerStyle={this.props.iconContainerStyle} closeIconButtonStyle={this.props.closeIconButtonStyle} rowTextStyle={this.props.rowTextStyle} rowStyle={this.props.rowStyle} />
</View>
</Modal>
</View>;
}
}
Response From Api :-
"configurable": [{
"id": "142",
"code": "size",
"label": "Size",
"options": [{
"attribute_id": "142",
"atribute_code": "size",
"id": "171",
"label": "XL",
"products": [
"2071",
"2074"
]
}, {
"attribute_id": "142",
"atribute_code": "size",
"id": "172",
"label": "L",
"products": [
"2072"
]
}]
},
{
"id": "93",
"code": "color",
"label": "Color",
"options": [{
"attribute_id": "93",
"atribute_code": "color",
"id": "50",
"label": "Blue",
"products": [
"2071"
]
},
{
"attribute_id": "93",
"atribute_code": "color",
"id": "60",
"label": "Black",
"products": [
"2072"
]
}, {
"attribute_id": "93",
"atribute_code": "color",
"id": "64",
"label": "Cyna",
"products": [
"2072"
]
}, {
"attribute_id": "93",
"atribute_code": "color",
"id": "61",
"label": "White",
"products": [
"2071",
"2074"
]
}
]
},
{
"id": "148",
"code": "format",
"label": "Format",
"options": [{
"attribute_id": "148",
"atribute_code": "format",
"id": "102",
"label": "Download",
"products": [
"2072",
"2071",
"2074"
]
},
{
"attribute_id": "148",
"atribute_code": "format",
"id": "103",
"label": "File",
"products": [
"2071",
"2074"
]
}
]
}
]
Your Help would be greatly appreciated !!!
Regards
When new state depends on previous state you should use the format setState((prevState, props) => {}) see reference in the docs. In any way you should never mutate state directly and this is what this line does selectValue[index] = value since selectValue is just a reference to this.state
So here is what exactly you need to do :-
// load selectedDropDownValue data from state with the item's id
// pass the item value to the change function
<Picker
selectedValue={this.state.selectedDropDownValue[item.id]}
onValueChange={(itemValue, itemIndex) => this.onClickDropdown(itemValue, itemIndex, item)}
>
{this.loadData(item)}
</Picker>
onClickDropdown(data, index, item){
// save each items selected data with their id
this.setState((prevState) => {
const value = Object.assign({}, prevState.selectedDropDownValue, { [item.id]: data});
return { selectedDropDownValue: value};
});
}