I am making custom radio button in react native.
Parent Component
const radioData = [
{ text: 'value A' },
{ text: 'value B' },
{ text: 'value C' },
];
<RadioButton
dataText={radioData}
isSelected={(selected) => {
console.log('<><>', selected);
}}
/>
Child Component
const RadioButton= (props) => {
const [selected, setSelected] = useState(false);
let { dataText, isSelected } = props;
return (
<>
{dataText.map((item) => {
return (
<View
style={{
flexDirection: 'row',
width: '50%',
marginVertical: 10,
}}
>
{selected ? (
<TouchableOpacity
onPress={() => {
if (selected) {
setSelected(false);
isSelected(false);
} else {
setSelected(true);
isSelected(true);
}
}}
>
<Image
source={require('../../assets/img/checkFullColor.png')}
style={{
width: 20,
height: 20,
marginRight: 20,
}}
resizeMode={'contain'}
/>
</TouchableOpacity>
) : (
<TouchableOpacity
onPress={() => {
if (selected) {
setSelected(false);
isSelected(false);
} else {
setSelected(true);
isSelected(true);
}
}}
>
<View
style={{
backgroundColor: Colors.accentDark,
height: 20,
width: 20,
borderRadius: 50,
marginRight: 20,
}}
/>
</TouchableOpacity>
)}
<Text style={{ color: Colors.accentDark }}>{item.text}</Text>
</View>
);
})}
</>
);
};
The Problem is even I click on any one of the RadioButton, then all 3 are getting selected or unselected.
I wanted a feature where I click on one of them then other gets unselected and selected value is updated in parent component.
CodePudding user response:
Your RadioButton
component should only render one radio button and not all of them at once. So you should map radioData
in the parent component. Right now, the selected
state is the same for every radio buttons mapped in your RadioButton
component.
The relevant part of your parent component:
const radioData = [
{ text: 'value A' },
{ text: 'value B' },
{ text: 'value C' },
];
return render (
<View>
{radioData.map((item) => { <- map here
<RadioButton dataText={item.text} />
})
</View>
);
And remove the mapping in your RadioButton
component.
/...
return (
<>
// {dataText.map((item) => { <- remove this
/...
If you wanted to have a component that render many radio buttons, keep the mapping in your RadioButton
component but also set state for every radio button. I would strongly recommend you to not do that and limit your RadioComponent
to one instance of a radio button.
CodePudding user response:
I think your issue is that you try to use one Boolean to keep track of whether of the currently selected RadioButton. If you want to use booleans to keep track, then every radioData item would need a boolean and every time a selection was made, then every other boolean would be need to be updated. An easier approach would be to just keep track of the selected index, and compare indices: (Here's a snack demo)
import React,{
useState,
useEffect
} from 'react';
import {
View,
Image,
TouchableOpacity,
// FlatList,
Text,
StyleSheet
} from 'react-native';
export default function RadioButton({onSelection,options, defaultSelection}){
// instead of giving each list item an isSelected prop just keep
// track of the selected index
const [selectedIndex, setSelectedIndex] = useState(defaultSelection || 0)
// its much cleaner to use an effect to allow subscriptions to changes
useEffect(()=>{
onSelection(selectedIndex,options[selectedIndex])
},[selectedIndex])
return (
<View style={styles.container}>
{
options.map((item,index)=>{
return (
<TouchableOpacity style={styles.rowItem} onPress={()=>setSelectedIndex(index)}>
{index == selectedIndex &&
<Image
source={require('../../assets/snack-icon.png')}
style={{
width: 20,
height: 20,
marginRight: 20,
}}
resizeMode={'contain'}
/>
}
<Text>{item.text}</Text>
</TouchableOpacity>
)
})
}
</View>
)
}
const styles = StyleSheet.create({
container:{
width:'100%',
height:'20%',
flexDirection: 'row',
flexWrap:'wrap',
justifyContent:'space-between',
alignItems:'center'
},
row:{
},
rowItem:{
// width: '50%',
paddingVertical:10,
marginVertical: 10,
}
})
And the parent component:
import * as React from 'react';
import { Text, View, StyleSheet } from 'react-native';
import Constants from 'expo-constants';
import RadioButton from './src/components/RadioButton'
export default function App() {
const radioData = [
{ text: 'value A' },
{ text: 'value B' },
{ text: 'value C' },
];
return (
<View style={styles.container}>
<RadioButton
options={radioData}
onSelection={(index,value) => {
console.log('<><>', value);
}}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
paragraph: {
margin: 24,
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
},
});