Home > Net >  Radio button not working properly in react native
Radio button not working properly in react native

Time:06-20

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',
  },
});

  • Related