How to use checkbox in loop in react naive - react-native

How to use checkbox in loop with diffrent key when i use in loop and click on any one after than check all loop checkbox give me solution

You can create an array of solutions in state:
...
state.solutions = [
{value:false},
{value:false},
{value:false}
]
Create a changes event handler:
changeEvent = (ev, index) => {
let tmp_solution = [...state.solutions];
tmp_solutions[index].value = !tmp_solutions[index].value;
this.setState({solutions: tmp_solution})
}
Create the checkboxes render function:
const checkBoxes = this.state.solutions.map((index) =>{
return(
<CheckBox
value={this.state.solutions[index].value}
onValueChange={(ev) => this.changeEvent(ev, index)} key={index}
/>
)
});
Render all the checkboxes:
render() {
<View>
{checkBoxes}
</View>
}
...
sorry if there are errors

Well, suppose that you have a data, so you can use map to do that. In order to save the answers also you can use index in setSetate as below. I use the react-native-checkbox-heaven component to have the check Box:
import CheckBox from 'react-native-checkbox-heaven';
renderContentCheckBox=()=>{
console.log("[renderContent] your data", this.state.data);
return Object.keys(this.state.data).map(function (item, index) {
return (<View key={index} style={{ alignItems: 'flex-start', justifyContent: 'flex-start', width: Dimensions.get('window').width / 1.1, }}>
<CheckBox
label={item.title}
labelStyle={styles.labelStyle}
iconSize={28}
iconName='matMix'
checked={this.state.check1}
checkedColor='#44FF00'
uncheckedColor='#FFFFFF'
onChange={(val) => {(value) => { that.setState({ ["CheckBox" + index]: value, }); console.log("radio" + index, value);
}}
/>
</View>
);
}
}
Then you can use it inside the render:
render() {
return (<View>
{this.renderContentCheckBox(this)}
</View>
);
}

Related

REACT NATIVE How can I highlight an item with a scrollview and increment/decrement the highlighted item?

I am building an 'Initiative Tracker' for home use. My goal is to have the first item at the top of the list highlighted by default and use a counter that keeps track of the turns to highlight the item to correspong with its turn.
Eg. When the '+' button is pressed, the next item should be highlighted and the previous item should return to normal.
I am currently using the map function to display an array. I feel like there should be a way to use the index and a style to achieve what I want, but I've had no luck so far.
Thanks in advance for any help or suggestions!
related code below:
let Encounter1Array = [
{ name: "goblin1", init: 5 },
{ name: "goblin2", init: 8 },
{ name: "goblin3", init: 15 },
{ name: "goblin4", init: 3 },
{ name: "goblin5", init: 9 },
];
function InitiativeTrackerScreen() {
const [encounter, setEncounter] = useState(Encounter1Array);
encounter.sort(function (x, y) {
return y.init - x.init;
});
return (
<KeyboardAvoidingView style={styles.wrapper}>
<ScrollView>
{encounter.map((item, index) => {
return (
<View key={index}>
<TouchableOpacity style={styles.ItemDisplayContainer}>
<View>
<View>
<Text style={{ fontStyle: "italic", fontSize: 16 }}>
{item.name}
</Text>
</View>
<View>
<Text style={{ fontSize: 12 }}>
Initiative: {item.init}
</Text>
</View>
</View>
</TouchableOpacity>
</View>
);
})}
</ScrollView>
</KeyboardAvoidingView>
);
}
You need extra state to keep track of the index of the element you want to highlight. Then you can use a conditional statement to match to right index and switch its style.
const P = ({ list }) => {
const [current, setCurrent] = useState(0);
return list.map((item, i) =>
<View style={i === current ? highlightStyle : style}>
<Button onPress={() => setCurrent(current + 1)}>
{"+"}
</Button>
{item}
</View>
);
};

Force FlatList to re-render after sorting data

I have a jobs app. In my JobsComponent, where I display the jobs, I have added a sort option that allows users to sort the list of jobs by different criteria. The flow is this: 1) I get the jobs from the server -> 2) the user sorts the jobs -> 3) the sorted list of jobs is re-rendered on the screen.
The problem is that step 3) is not working. The actual list of jobs is being sorted (I can see that in the logs), but my FlatList is not being re-rendered.
What I have tried
I have a flag, sortOrderChanged, set in my state. Whenever the user selects a sorting option, I change this flag in my componentDidMount() method:
this.setState({
sortOrderChanged: !this.state.sortOrderChanged,
selectedSortOrder: dataFromChild
});
and pass it to FlatList as extraData:
<FlatList
data={sort_array}
extraData={props.sortOrderChanged}
renderItem={renderJobItem}
keyExtractor={(item, index) => index.toString()}
style={{marginTop: 10}}
/>
This does not help though. I have also tried sending the whole state to the FlatList and passing it to extraData, but it also didn't work. I assume the problem is that my data is not actually being changed, but sorted. However, I do not know how to force it to re-render. Can someone help me out, please?
Below is my JobsComponent.js:
function RenderJobs(props) {
var json = JSON.parse(props.jobsData);
var sort_array = [];
for (var _id in json) {
sort_array.push({
_id:_id,
jobtitle: json[_id].jobtitle,
company: json[_id].company,
duration_driving_value:json[_id].duration_driving.value,
duration_transit_value: json[_id].duration_transit.value,
duration_walking_value: json[_id].duration_walking.value,
duration_driving:json[_id].duration_driving.text,
duration_transit:json[_id].duration_transit.text,
duration_walking:json[_id].duration_walking.text,
date: json[_id].date,
formatedDescription: json[_id].formatedDescription,
applyUrl: json[_id].applyUrl
});
}
//sort the list based on user selection
if (props.sortOrder === props.sortArray[0]) {
sort_array.sort(function(x,y){return new Date(y.date) - new Date(x.date)});
}
else if (props.sortOrder === props.sortArray[1]) {
sort_array.sort(function(x,y){return x.duration_driving_value - y.duration_driving_value});
}
else if (props.sortOrder === props.sortArray[2]) {
sort_array.sort(function(x,y){return x.duration_transit_value - y.duration_transit_value});
}
else {
sort_array.sort(function(x,y){return x.duration_walking_value - y.duration_walking_value});
}
const renderJobItem = ({item}) => {
var durationCarApi, durationPublicTransportApi, durationWalkApi, formattedApiDate, formattedJobDescription;
//format data
return (
<Panel //custom component used to display each job
jobTitle={item.jobtitle}
company={item.company}
durationCar={durationCarApi}
durationTram={durationPublicTransportApi}
durationWalking={durationWalkApi}
dateAdded={formattedApiDate}
onPress={() =>
{
props.navigation.navigate('JobDetails', {
jobTitle: item.jobtitle,
company: item.company,
durationCar: durationCarApi,
durationTram: durationPublicTransportApi,
durationWalking: durationWalkApi,
jobDescription: formattedJobDescription,
applyUrl: item.applyUrl
})
}
}/>
);
}
//handle loading/error scenarios
return (
<FlatList
data={sort_array}
extraData={props.sortOrderChanged}
renderItem={renderJobItem}
keyExtractor={(item, index) => index.toString()}
style={{marginTop: 10}}
/>
);
}
class Jobs extends Component {
constructor(props) {
super(props);
this.state = {
jobTitle: this.props.navigation.getParam('jobTitle', ''),
address: this.props.navigation.getParam('address', 'error'),
sortOrderChanged: false,
sortArray: [0,1,2,3],
selectedSortOrder: 1 //default is sort_driving
};
}
componentDidMount() {
handleSorting = (dataFromChild) => {
console.log('Sort order clicked: ' + dataFromChild);
this.RBSheet.close();
this.setState({
sortOrderChanged: !this.state.sortOrderChanged,
selectedSortOrder: dataFromChild
});
}
render() {
return(
<ScrollView contentContainerStyle={styles.bkg}>
<RenderJobs
jobsData={JSON.stringify(this.props.jobs.jobs)}
isLoading={this.props.jobs.isLoading}
errMess={this.props.jobs.errMess}
navigation={this.props.navigation}
sortOrder={this.state.selectedSortOrder}
sortArray={this.state.sortArray}
sortOrderChanged={this.state.sortOrderChanged}
/>
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<RBSheet //custom component used to render the sorting options
ref={ref => {this.RBSheet = ref;}}
height={200}
duration={250}
customStyles={{
container: {
justifyContent: "center",
alignItems: "center"
}
}}>
<SortSheet //this is the child component used to render the sorting options
sortOrder={this.handleSorting}
sortArray={this.state.sortArray}/>
</RBSheet>
</View>
</ScrollView>
)
}
}
Move your data from local variable to state.
Or add forceUpdate after else.
The solution was to change the keyExtractor to item._id:
<FlatList
data={props.jobsData}
extraData={props.sortOrderProps}
renderItem={renderJobItem}
keyExtractor={(item, index) => item._id}
style={{marginTop: 10}}
/>

React Native stored array data with asyncstorage returns nothing

I am trying to build a lunch picker app that allows user to add their own menu. I want to save user data into array by using AsyncStorage. However, my value returns nothing even though the array has values. Below is my code.
//Main screen
class HomeScreen extends React.Component {
//initial
constructor(props) {
super(props);
this.state = {
isReady: false,
myMenu: '????',
menutext: '',
randomArray: ['a', 'b', 'c'],
visibility: false,
};
}
_loadMenu = async () => {
try{
const loadMenu = await AsyncStorage.getItem("menuInStorage")
const parsedLoadMenu = JSON.parse(loadMenu)
const myReturn = [...this.state.randomArray, parsedLoadMenu]
this.setState({randomArray: myReturn})
}
catch(err){
alert(err)
}
}
//get input from textinput field and add to array
addMenu = newMenu => {
//...
this._saveMenu(this.state.randomArray)
};
_saveMenu = (saving) => {
const saveMenu = AsyncStorage.setItem("menuInStorage", JSON.stringify(saving))
}
//control modal
setModalVisibility(visible) {
this.setState({visibility: visible});
}
//UI
render() {
return (
<View style={styles.mainContainer}>
<View style={[styles.container, {flexDirection: 'row', justifyContent: 'center'}]}>
<TextInput
style={{ height: 40, fontSize: 20, paddingLeft: 15, textAlign: 'left', width: 250, borderBottomColor: '#D1D1D1', borderBottomWidth: 1 }}
placeholder=".."
onChangeText={menutext => this.setState({ menutext })}
value={this.state.menutext}
/>
<Button
title=".."
onPress={() => this.addMenu(this.state.menutext)}
buttonStyle={{width:100}}
backgroundColor="#2E282A"
/>
</View>
<Text>{'\n'}</Text>
<Button
onPress={() => this.setModalVisibility(true)}
title=".."
buttonStyle={{width: 150}}
backgroundColor="#2E282A"
/>
</View>
<Modal
onRequestClose={() => this.setState({ visibility: false })}
animationType={'slide'}
transparent={false}
visible={this.state.visibility}
>
<View style={[styles.modalContainer, {marginBottom: 100}]}>
<Text style={[styles.text, { fontWeight: 'bold', padding: 20, backgroundColor: '#9090DA', borderBottomColor: '#5C5C8B',
borderBottomWidth: 1,}]}>
{'<'}List will be here{'>'}
</Text>
<ScrollView style={{height: "94%"}}>
<View style={styles.row}>{this.state.randomArray}</View>
</ScrollView>
<Button
buttonStyle={{justifyContent: 'center', marginTop: 5}}
backgroundColor="#2E282A"
onPress={() => this.setModalVisibility(!this.state.visibility)}
title="Close"
/>
</View>
</Modal>
</View>
);
}
}
How the app supposed to work is, when user clicks a button, the modal shows all data in array called 'randomArray'. After user added their custom text, it should be added at the end of the randomArray. I want to save this data to the disk and load from the disk when the app is launched. At this moment, I can load array data, but it doesn't keep user data. My current code returns nothing. I need your help. Thanks.
It looks like the logic in _loadMenu is slightly incorrect on this line:
const myReturn = [...this.state.randomArray, parsedLoadMenu]
If I understand correctly, you're expecting parsedLoadMenu to be a value of type Array. The line above will basically append the value parsedLoadMenu to the resulting array stored in myReturn - in the case of your code, this will mean the last item of myReturn will be an array, which would be incorrect from what I see in your code. Consider updating this line as shown:
/*
Add ... before parsedLoadMenu to concatenate the two arrays in myReturn
*/
const myReturn = [...this.state.randomArray, ...parsedLoadMenu]
By adding the ... as shown, this causes the two arrays this.state.randomArray and parsedLoadMenu to be concatenated together in myReturn. It would also be worth checking the parse result from JSON.parse() to ensure that it is an array before attempting this concatenation:
_loadMenu = async () => {
try{
const loadMenu = await AsyncStorage.getItem("menuInStorage")
let parsedLoadMenu = JSON.parse(loadMenu)
/*
Consider an additional check here to ensure the loaded data is of
correct Array type before proceeding with concatenation
*/
if(!Array.isArray(parsedLoadMenu)) {
parsedLoadMenu = [];
}
/* Concatenate the two arrays and store result in component state */
const myReturn = [...this.state.randomArray, ...parsedLoadMenu]
this.setState({randomArray: myReturn})
}
catch(err){
alert(err)
}
}
Also, consider revising the addMenu logic, so that the entire array of menu items in your is persisted to AsyncStorage rather than the newly added menu item only, as you are currently doing:
addMenu = (newMenu) => {
/*
Persist current randomArray with newMenu item appended
*/
this._saveMenu([...this.state.randomArray, newMenu])
};
Hope this helps!

React-native FlatList item changes position after update

I have created a flatlist and each item has a button that onPress will change item value. The issue comes up when I change item value, as that item will also unexpectedly fall down to the last position of the flatlist.
This is happening only from React-Native version 57 +
The 55.4 version didnt' run into this problem, but I need to upgrade react-native to 57+.
Thanks in advance for any help :)
Here is the sample code:
import React, { Component } from 'react';
import { View, Text, FlatList, StyleSheet, Button } from 'react-native';
class List extends Component {
state = {
currencies: {
USD: 0,
EUR: 0,
GBP: 0,
BTC: 0,
CNY: 0,
AUD: 0,
JPY: 0
}
}
render() {
return (
<View style={styles.container}>
<FlatList
extraData={this.state}
data={Object.keys(this.state.currencies)}
style={{ flex: 1 }}
renderItem={({ item }) => (
<Row
itemValue={this.state.currencies[item]}
itemName={item}
numberUp={(name) => this.setState({currencies:
{...this.state.currencies,
[name] : this.state.currencies[name] + 1}
})
}
/>
)}
/>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flexDirection: "row",
},
itemContainer : {
flexDirection: "row",
justifyContent: "center",
margin: 10
},
itemText : {
fontSize: 30
}
});
export default List;
const Row = (props) => (
<View style={styles.itemContainer}>
<Text style={styles.itemText}>{props.itemName} </Text>
<Text style={styles.itemText}>{props.itemValue} </Text>
<Button title="PLUS" onPress={() => props.numberUp(props.itemName)}/>
</View>
);
you are trying to append item into the list. that's why it is displaying item at the end of the list.
try below code, I have tried it and it's working:
render() {
return (
<View style={styles.container}>
<FlatList
extraData={this.state}
data={Object.keys(this.state.currencies)}
style={{ flex: 1 }}
renderItem={({ item }) => (
<Row
itemValue={this.state.currencies[item]}
itemName={item}
numberUp={(name) => {
let currencyList = this.state.currencies;
currencyList[name] = this.state.currencies[name] + 1;
this.setState({currencies: currencyList})
}
}
/>
)}
/>
</View>
)
}
Object doesn't maintain the property order. Thats why order gets changed in your list when you update it. ECMA script defines object as
An object is a member of the type Object. It is an unordered
collection of properties each of which contains a primitive value,
object, or function. A function stored in a property of an object is
called a method.
So to maintain the order its better to use an array instead.
currencies: [
{currency:USD, value:0},
{currency:EUR, value:0},
{currency:GBP, value:0},
{currency:BTC, value:0},
{currency:CNY, value:0},
{currency:JPY, value:0},
]

React Native - SectionList numColumns support

FlatList has numColumns support. How to set numColumns with SectionList?
Github issue: SectionList renderItem multi item support #13192
Here is my solution to numColumns for SectionList. If you have better let me know please.
class Example extends Component {
static propTypes = {
numColumns: PropTypes.number
};
static defaultProps = {
numColumns: 2
};
_renderSection = data => <Section {...data} />;
_renderItem = ({ section, index }) => {
const { numColumns } = this.props;
if (index % numColumns !== 0) return null;
const items = [];
for (let i = index; i < index + numColumns; i++) {
if (i >= section.data.length) {
break;
}
items.push(<Item item={section.data[i]} />);
}
return (
<View
style={{
flexDirection: "row",
justifyContent: "space-between"
}}
>
{items}
</View>
);
};
render() {
return (
<SectionList
sections={dumyData}
style={styles.container}
renderItem={this._renderItem}
renderSectionHeader={this._renderSection}
/>
);
}
}
It is possible to use FlatList with numColumns prop as the renderItem of SectionList.
const data = [ //Notice [[...]] instead of [...] as in the RN docs
{data: [[...]], title: ...},
{data: [[...]], title: ...},
{data: [[...]], title: ...},
]
render () {
return (
<SectionList
renderItem={this._renderSectionListItem}
renderSectionHeader={this._renderSectionHeader}
sections={data}
/>
)
}
renderSectionListItem = ({item}) => {
return (
<FlatList
data={item}
numColumns={3}
renderItem={this.renderItem}
/>
)
}
Digging this issue up, I came with a solution similar to Pir Shukarullah Shah 's.
I'm using FlatList instead of my regular item, taking into account only the first item in <SectionList/>'s renderItem method.
_renderList = ({ section, index }) => {
if (index !== 0) return null;
return (
<FlatList numColumns={columns}
columnWrapperStyle={styles.container}
data={section.data}
renderItem={this._renderItem}
keyExtractor={keyExtractor}
/>
)
}
...
<SectionList
renderItem={this._renderList}
renderSectionHeader={this._renderSectionHeader}
sections={itemList}
keyExtractor={keyExtractor}
/>
I found there is a simple solution. Please try adding the following property to the
contentContainerStyle={{
flexDirection : 'row',
justifyContent : 'flex-start',
alignItems : 'flex-start',
flexWrap : 'wrap'
}}
Besides, set and render the Section Header with the Width equal to the SectionList width. Otherwise, the list items will be displayed following the Section Header in row direction.
const DATA = [
{
renderItem: ({ item, index }) => {
return (<View style={{flexDirection:'row', alignItems:'center', justifyContent:'space-between', }}>
{item.map((elem,index)=>(<View style={{ borderColor: 'black', borderWidth: 2, minWidth:100 }}>
<Text>{elem.value}</Text>
</View>))
}
</View>);
},
data: [
[{id:'1', value:'Pizza'}, {id:'2', value:'Burger'}, {id:'3', value:'Onion Rings'}], //this array length will be noOfColumns
[{id:'4', value:'Risotto'}, {id:'5', value:'French Fries'}, {id:'6', value:'Water'}],
],
},
<SectionList
ref={listRef}
sections={DATA}
keyExtractor={_keyExtractor}
/>
I had the same logic like Pir Shukarullah Shah. The idea of using flexWrap is not recommended by react and warns to use numColumns prop in flatlist. If anyone has a better solution please add.
let items = []
const renderItem = ({ item, index }) => {
if (index % 2 === 0) {
items = []
items.push(<Card cloth={item} index={index} />)
return (index === clothes[0].data.length - 1) ? <View style={styles.row}>{items}</View> : null
}
items.push(<Card cloth={item} index={index} />)
return (
<View style={styles.row}>
{items}
</View>
)
}
The section list is :
<SectionList
sections={clothes}
renderItem={renderItem}
keyExtractor={(item, index) => index}
renderSectionHeader={renderSectionHeader}
stickyHeaderHiddenOnScroll={true}
stickySectionHeadersEnabled={true}
onEndReached={endReachedHandler}
onEndReachedThreshold={0.25}
contentContainerStyle={{ paddingBottom: '25%' }}
/>
The structure for clothes is:
let one = {name: 'Jeans pant'}
let many = Array(10).fill(one) // creating more dummy clothes
let cl = [{data: many, title: 'Cloth'}]
let [clothes, setClothes] = useState(cl)
I needed only one section so in cl array I wrote only one object initially if you want to have multiple sections you would need to add to the clothes array.
This is a slightly updated version of Pir Shukarullah Shah accepted answer to show a more functional approach over class approach.
// render a single section.data item
const itemRenderer = (item) => <Text>{item}</Text>
return (
<SectionList
sections={listData}
renderItem={(section, index) => {
if (index % numCols) { // items are already consumed
return null
}
// grab all items for the row
const rowItems = section.data.slice(index, index+numCols)
// wrap selected items in a "row" View
return <View
style={{
flexDirection:"row",
justifiyContent:"space-between"
}}
>{rowItems.map(itemRenderer)}</View>
}}
/>)
Also if you have fixed width items you can calculate numCols dynamically here's an example for a full screen width SectionList:
const itemFixedWidth = 24
const listWidth = useWindowDimensions().width
const numCols = Math.floor(listWidth / itemFixedWidth)
I'm a new user to this site, otherwise I'd just upvote Fong's answer above. Slick, that one.
Just to further clarify the last sentence he wrote.
I used Dimensions.get('window').width on the section header like so:
renderSectionHeader={({ section: { title } }) => (
<View
style={{
width: Dimensions.get('window').width,
}}
>
<Text>
{title}
</Text>
</View>
)}
Though that method does throw a console warning about using flexWrap...