How to Dynamically create checkboxes in React-Native - javascript

function Screen() {
const [lvls, setLvls] = React.useState([]);
const [done, setDone] = React.useState(null);
const [toggleCheckBox, setToggleCheckBox] = React.useState(false)
React.useEffect(() => {
async function checkData() {
const data = await AsyncStorage.getAllKeys();
setLvls(data);
}
checkData();
setDone(true);
}, []);
return (
<View>
{done && (lvls.length) ? (
lvls.map((element, i) => {
return (
<View key={i} style={{flexDirection: 'row'}}>
<CheckBox
disabled={false}
value={toggleCheckBox}
onValueChange={(newValue) => setToggleCheckBox(newValue)}
/>
<Text style={styles.text}>{lvls[i]}</Text>
</View>)
})
):
(
<Text style={styles.emptyText}>NO</Text>
)}
</View>
);
}
My aim is to dynamically render a checkbox that corresponds to each element in lvls.
I am currently doing that, however all of the checkboxes share the same state toggleCheckbox.
How can I create unique states for each checkbox according to lvls?

You can create a object that will store all the checkboxes values.
const [toggleCheckBox, setToggleCheckBox] = React.useState({})
<CheckBox
disabled={false}
value={toggleCheckBox[i] ? true : false}
onValueChange={() =>
setToggleCheckBox({
...toggleCheckBox,
[i] : !toggleCheckBox[i]
})}
/>
Placing into your code...
function Screen() {
const [lvls, setLvls] = React.useState([]);
const [done, setDone] = React.useState(null);
const [toggleCheckBox, setToggleCheckBox] = React.useState({})
React.useEffect(() => {
async function checkData() {
const data = await AsyncStorage.getAllKeys();
setLvls(data);
}
checkData();
setDone(true);
}, []);
return (
<View>
{done && (lvls.length) ? (
lvls.map((element, i) => {
return (
<View key={i} style={{flexDirection: 'row'}}>
<CheckBox
disabled={false}
value={toggleCheckBox[i] ? true : false}
onValueChange={() =>
setToggleCheckBox({
...toggleCheckBox,
[i] : !toggleCheckBox[i]
})}
/>
<Text style={styles.text}>{lvls[i]}</Text>
</View>)
})
):
(
<Text style={styles.emptyText}>NO</Text>
)}
</View>
);
}

Related

Accordion inside a Flatlist React native

I have an accordion inside a flatlist.
Here is the code i have :
const OptionList = (groupOption) => {
return (
<FlatList
data={groupOption.options}
keyExtractor={(result) => result.id.toString()}
renderItem={({ item, index }) => {
const clickedRadio = () => {
const selectedOption = { [item.question]: { ...item } };
setAnswers({ ...answers, ...selectedOption });
};
const status = isOptionSelected(item) ? true : false;
return (
<View key={index}>
<Radio
initialValue={status}
label={item.description}
onChange={() => clickedRadio()}
color="error"
/>
</View>
);
}}
/>
);
};
return (
<View style={styles.container}>
<Text style={{ fontWeight: "bold", fontSize: 16, color:"#6B24AA" }}>
{t("Choose an option/Scroll for more questions")}
</Text>
<FlatList
data={questions}
listKey={(item) => item.id.toString()}
keyExtractor={(result) => result.id.toString()}
renderItem={({ item, index }) => {
const data = [
{
title: item.description,
content: (<><OptionList options=
{item?.question_options}></OptionList></>)
}
];
const status = isOptionSelected(item) ? true : false;
return (
<View style={styles.groupOptions} key={index}>
<Accordion style={styles.accordion}
headerStyle=
{styles.headerStyle} contentStyle={styles.contentStyle}
dataArray={data}
icon={status ? <Icon name="circle"
family="Entypo" size={20} style={{marginLeft: -6,
color: "#6B24AA"}}/>
:
<Icon name="done"
family="MaterialIcons" size={20}
style={{marginLeft: -6}}/>}
expandedIcon={<Icon name="circle"
family="Entypo"/>}
opened={1}/>
</View>
);
}}
/>
</View>
);
The accordion content its anther flatlist component. It shows this error every time i click the accordion.
It shows this error :
VirtualizedList: Encountered an error while measuring a list's offset from its containing VirtualizedList.
at node_modules/react-native/Libraries/Lists/VirtualizedList.js:1411:10 in _scrollRef.measureLayout$argument_2
How can i fix this error? Is it the problem the other flatlist at the content of accordion
Please replace the OptionList component with the given below code.
OptionList
const OptionList = (groupOption) => {
return (
groupOption.map((item,index) => {
const clickedRadio = () => {
const selectedOption = { [item.question]: { ...item } };
setAnswers({ ...answers, ...selectedOption });
};
const status = isOptionSelected(item) ? true : false;
return (
<View key={index}>
<Radio
initialValue={status}
label={item.description}
onChange={clickedRadio}
color="error"
/>
</View>
)
})
);
};
please check and let me know , cheers !

Looping return from AsyncStorage

i have following code returning value from AsyncStorage
const getData = async () => {
try {
let listlayana = JSON.parse(await AsyncStorage.getItem('layanan'));
return listlayana
}catch (e){
}
}
const datas = getData()
and i trying to loop this function result with this
{
datas.map((item, i) => (
<TouchableOpacity key={i}>
<View key={i} style={styles.listitem}>
<Text style={styles.listtext} >{item.nmlayanan}</Text>
<Text style={[styles.listtext, {color: "#b4b3b3"}]}> > </Text>
</View>
</TouchableOpacity>
))
}
and i got this following error
undefined is not a function (near '...datas.map...')
You can use React.useEffect and React.useState for this target like this:
const [data, setData] = React.useState(); //add this useState for the data
const getData = async () => {
try {
let listlayana = JSON.parse(await AsyncStorage.getItem("layanan"));
return listlayana;
} catch (e) {}
};
// and add this async useeffect for fetching data from asyncstorage
React.useEffect(async () => {
const scopeData = await getData();
setData(scopeData);
}, []);
and then your render function like this:
{data.map((item, i) => (
<TouchableOpacity key={i}>
<View key={i} style={styles.listitem}>
<Text style={styles.listtext}>{item.nmlayanan}</Text>
<Text style={[styles.listtext, { color: "#b4b3b3" }]}> > </Text>
</View>
</TouchableOpacity>
))}
and your component like this:
export default function SOF() {
const [data, setData] = React.useState();
const getData = async () => {
try {
let listlayana = JSON.parse(await AsyncStorage.getItem("layanan"));
return listlayana;
} catch (e) {}
};
React.useEffect(async () => {
const scopeData = await getData();
setData(scopeData);
});
return (
<View>
{data.map((item, i) => (
<TouchableOpacity key={i}>
<View key={i} style={styles.listitem}>
<Text style={styles.listtext}>{item.nmlayanan}</Text>
<Text style={[styles.listtext, { color: "#b4b3b3" }]}> > </Text>
</View>
</TouchableOpacity>
))}
</View>
);
}

Is there a way to use a map inside of another map function?

What I'm trying to do is after a field name, I want to render its type which is in another state as you can see:
my code:
const FormScreen = ({route}) => {
const [FieldForm, setFieldForm] = useState([]);
const [TypeForm, setTypeForm] = useState([]);
useEffect(() => {
if (userForm.length > 0) {
return;
} else {
setFieldForm(JSON.parse(route.params.paramKey).fields);
setTypeForm(JSON.parse(route.params.paramKey).type);
console.log(FieldForm,TypeForm); // returns me: {"message":["ab","test"],"tipo":["Date","Text"]}
}
},[userForm,TypeForm]);
return (
<SafeAreaView style={{flex: 1}}>
<View>
<Text>COLLECTION :</Text>
{FieldForm.length > 0 ? (
FieldForm.map((item) => (
<Text key={uuid.v4()}>{item}</Text>
//Here I want to to map which type is the field(to use conditionals to render) but how can I make it?
))
) : (
<Text key={uuid.v4()}> Loading ...</Text>
)}
</View>
</SafeAreaView>
);
};
How can I use a map inside of a map to do it, or creating an api call to return the type of the fields?
You can definitely nest the map functions :
const FormScreen = ({route}) => {
const [FieldForm, setFieldForm] = useState([]);
const [TypeForm, setTypeForm] = useState([]);
useEffect(() => {
if (userForm.length > 0) {
return;
} else {
setFieldForm(JSON.parse(route.params.paramKey).fields);
setTypeForm(JSON.parse(route.params.paramKey).type);
console.log(FieldForm,TypeForm); // returns me: {"message":["ab","test"],"tipo":["Date","Text"]}
}
},[userForm,TypeForm]);
return (
<SafeAreaView style={{flex: 1}}>
<View>
<Text>COLLECTION :</Text>
{FieldForm.length > 0 ? (
FieldForm.map((item) => (
<>
<Text key={uuid.v4()}>{item}</Text>
<> {TypeForm.length && TypeForm.map((type) => ( // Return node )) || null};
</>
</>
))
) : (
<Text key={uuid.v4()}> Loading ...</Text>
)}
</View>
</SafeAreaView>
);
};

Apply corresponding color and icon when item is selected

I am working on the modal picker component that lets users sort the cryptocurrencies based on Market Cap and Volume key.
I have implemented the layout and onPress event, but I don't know how to apply color (#03AE9D) and toggle sort icons (sort-desc or sort-asc) on it properly when the item is selected.
Here is my code snippet. For the sake of simplicity, I removed the StyleSheet code.
The complete version could be found here.
// app/components/ModalPicker.js
const OPTIONS = ['market cap', 'volume'];
const ModalPicker = (props) => {
const [isSelected, setSelected] = useState(false);
const color = useMemo(() => {
return isSelected ? '#03AE9D' : '#676767cf';
}, [isSelected]);
const onPressCallback = useCallback(() => {
setSelected((prev) => !prev);
}, [setSelected]);
const onPressItem = (option) => {
props.changeModalVisibility(false);
props.setData(option);
};
const option = OPTIONS.map((item, index) => {
return (
<TouchableOpacity
style={
index === OPTIONS.length - 1 ? styles.noBorderOption : styles.option
}
key={index}
onPress={() => onPressItem(item)}
>
<View style={styles.sort}>
<Text style={[styles.text, { color }]}>{item}</Text>
{/* <FontAwesome name='sort-desc' size={24} color='#676767cf' /> */}
{/* <FontAwesome name='sort-asc' size={24} color='#676767cf' /> */}
</View>
</TouchableOpacity>
);
});
return (
<TouchableOpacity
onPress={() => props.changeModalVisibility(false)}
style={styles.container}
>
<View style={styles.modal}>
<ScrollView>{option}</ScrollView>
</View>
</TouchableOpacity>
);
};
export default ModalPicker;
That's the whole point of the question. Any suggestions are welcome.
For anyone else having this issue. You can add a active index state and set it in the onPress callback:
const [activeIndex, setActiveIndex] = useState();
const onPressItem = (option, index) => {
setActiveIndex(index);
setSelected(true);
//props.changeModalVisibility(false);
//props.setData(option);
};
Then use it in the items map:
const option = OPTIONS.map((item, index) => {
return (
<TouchableOpacity
style={
index === OPTIONS.length - 1 ? styles.noBorderOption : styles.option
}
key={index}
onPress={() => onPressItem(item, index)}
>
<View style={styles.sort}>
<Text style={activeIndex === index && isSelected ? styles.text : styles.textInactive}>{item}</Text>
{/* <FontAwesome name='sort-desc' size={24} color='#676767cf' /> */}
{/* <FontAwesome name='sort-asc' size={24} color='#676767cf' /> */}
</View>
</TouchableOpacity>
);
});
See a snack here for working example https://snack.expo.io/#yentln/jealous-candy

React Native, button changing for all items in list

I created a flat list in which if user click buy button it should change but it's changing for every items. It should only change that item user buy. Can someone tell me what's wrong in my code, below is my code
FlatList
<FlatList
data={this.props.items}
key={(items) => items.id.toString()}
numColumns={2}
renderItem={({ item }) => (
<CardBuyItem>
<Image style={styles.image} source={item.image} />
<View style={styles.detailContainer}>
<Text style={styles.title}>{item.title}</Text>
<Text style={styles.subTitle} numberOfLines={1}>
{item.subTitle}
</Text>
<Text style={styles.price}>Rs {item.price}</Text>
</View>
{this.props.button && this.props.added.length > 0 ? (
<View style={styles.add}>
<Text style={styles.quantity}>{item.quantity}</Text>
<MaterialCommunityIcons
style={styles.iconUp}
size={20}
name="plus-circle-outline"
onPress={() => this.props.addQuantity(item.id)}
/>
<MaterialCommunityIcons
style={styles.iconDown}
size={20}
name="minus-circle-outline"
onPress={() => this.props.subtractQuantity(item.id)}
/>
</View>
) : (
<View style={styles.buy}>
<Text
style={styles.buyonce}
onPress={() => {
this.props.addToCart(item.id);
this.props.showCart();
this.props.showButton(item.id);
}}
>
Buy Once
</Text>
</View>
)}
</CardBuyItem>
)}
/>
const mapStateToProps = (state) => {
return {
items: state.clothes.jeans,
button: state.clothes.showButton,
added: state.clothes.addedItems,
};
};
const mapDispatchToProps = (dispatch) => {
return {
addToCart: (id) => dispatch(addToCart(id)),
addQuantity: (id) => dispatch(addQuantity(id)),
subtractQuantity: (id) => dispatch(subtractQuantity(id)),
showCart: () => dispatch(showCart()),
showButton: (id) => dispatch(showButton(id)),
};
};
that's my flat list where button should change for that particular item
reducer
if (action.type === SHOW_BUTTON) {
let addedItem = state.jeans.find((item) => item.id === action.id);
return {
...state,
addedItem: addedItem,
showButton: action.showButton,
};
}
const initialstate = { showButton: false}
that's my reducer
action
export const showButton = (id) => {
return {
type: SHOW_BUTTON,
showButton: true,
id,
};
};
and that's my action for reducer
Can someone please tell me what's wrong with it?
Try to add count to your item and in your reducer put
item.forEach(cp => {
if (cp.id === action.id) {
cp.count += 1;
}
});

Categories