I added a custom radio button to each of my flatlist items. So there's a radio button on the left of each item row and I want to be able to toggle between the selected value of the radio button depending on row item. So, I can select the first row item and that's selected/toggled on or select the second one item and that's toggled on and the first row item is toggled off.
I'm a bit confused on how to set up the logic for this with my current object. My current object has an id and isSelected bool. The bool will indicate if the row is isSelected or not (i.e. is checked or not).
Here's what i have so far:
This is my component that renders the flatlist fooItems. The fooItems itself is the individual elements that make up the list.
export class FooItemsComponent extends Component {
constructor(props) {
super(props);
}
async componentDidMount() {
//some reducer action
}
renderItem = item => {
if (item.load) {
return <ActivityIndicator color={Colors.activityColor} size="small" />;
} else {
return (
<View>
<FooItem
{...item}
navigation={this.props.navigation}
showParticularItems={true}
/>
</View>
); }
};
render() {
const { fooItems, navigation, loading, props} = this.props;
if (!fooItems) fooItems = [];
return (
<View style={styles.container}>
<FlatList
keyExtractor={(item, index) => `key-${index}`}
data={fooItems}
renderItem={item => this.renderItem(fooItems.item)}
ItemSeparatorComponent={Separator}
ListHeaderComponent={Header}
ListFooterComponent={loading ? LoadingFooter : Separator}
/>
</View>
);
}
}
FooItem component:
export default class FooItem extends Component {
constructor(props) {
super(props);
this.state = {
isSelected: this.props.fooItems.isSelected
};
}
changeSelected = isSelected => {
if(!isSelected) {
let selected = this.props.fooItems;
selected = selected.isSelected = true;
this.setState({ isSelected: selected});
}
};
render() {
const {
Details,
showPreferred = false,
showSetSelected = false,
navigation
} = this.props;
const { isSelected} = this.state;
return (
<View
style={[ showSetSelected? styles.setDefaultContainer :
styles.container,
showBorder ? { borderBottomWidth: 1, borderTopWidth: 1 } : {},
]}
>
{showSetSelected && (
<View>
<TouchableOpacity
style={[styles.info, !isSelected ? styles.lineBreak : {}]}
onPress={() => this.changeSelected(isSelected)}
>
<RadioButton isChecked={isSelected}></CheckedCircle>
</TouchableOpacity>
</View>
)}
</View>
);
}
}
My custom control:
const RadioButton = ({ isChecked }) => {
let checkedStatus;
switch (isChecked) {
case true:
checkedStatus = <Image style={styles.container} source={require('../../../assets/radioButton.png')} />
break
default:
checkedStatus = <Image style={styles.container} source={require('../../../assets/circle.png')} />
break
}
return <View style={styles.container}>{checkedStatus}</View>
}
Right now, I'm noticing the other row that is not already selected and I select it, the radio button state for that particular row is checked but now I have two checked/selected rows instead of one. So I want to know how to toggle the other row. How can I get this done?
EDIT:
Example of radio button toggle in a list where I only want to toggle between listed radio buttons between rows of the list.
Maintain a state variable such as selectedIndex or some unique id in your FooItemsComponent and pass this index/id to each of your FooItem and compare against this selectedIndex as
<RadioButton
isChecked={item.id === this.props.selectedIndex}
/>
Maintain a method at FooItemsComponent level which controls which item is checked as follows
changeSelected = index => {
this.setState({
selectedIndex: index,
});
};
and pass the same to every FooItem
<FooItem
{...item}
...
selectedIndex={this.state.selectedIndex}
changeSelected={this.changeSelected}
/>
and finally in the FooItem put this on a Touchable and pass the FooItem's id or index.
<TouchableOpacity onPress={() => this.props.changeSelected(item.id)}>
Here is a expo link for rough idea.(Please ignore the styles and make it truly yours!)
You need to keep track of which FooItem is selected in the state of your FooItemsComponent.
Then use that to check
<FooItem
{...item}
isSelected={this.state.selectedItem.id === item.id}
navigation={this.props.navigation}
showParticularItems={true}
/>
Or something in this fashion.
Related
I have a list with some items that are added to a state on click, Im using native-base, how can I change the style of the listitem when I press it, or add the "selected" attribute to the list item?
code
const [data, setData] = useState([]);
const _renderItem = ({ item, index }) => {
return (
<ListItem
button={true}
onPress={() => handleItemSelect(item)}
>
<Left>
<Text>{item.Name}</Text>
</Left>
<Right>
<Icon name="add" style={{ paddingHorizontal: 5 }} />
</Right>
</ListItem>
);
};
return(
<Container>
<List>
<FlatList
data={data}
renderItem={_renderItem}
/>
</List>
</Container>
);
Im wondering how am I going to add a style and distinguish between the different list items that I have, if this isn't possible how can I use native-base "selected" and append it to the listitem?
the handleItemSelect adds the item id to a state so im currently managing which items are selected, how can I use this info, or any other way to highlight the selected items?
Edit:
I figured how to easily do this since I have the selected items id's
<ListItem
selected={selectedItems.some((prevItem) => prevItem._id === item._id)}
style={sameasabove ? style1 : style2}
button={true}
onPress={() => handleItemSelect(item)}
>
</ListItem>
You can do some thing like this:
Example
export default class App extends React.Component {
constructor() {
super();
this.state = {
data: [
{ name: "Interstellar" },
{ name: "Dark Knight" },
{ name: "Pop" },
{ name: "Pulp Fiction" },
{ name: "Burning Train" },
],
setData: []
};
for (var i = 0; i < this.state.data.length; i++) {
this.state.setData[i] = "red";
}
this.setState({
setData: this.state.setData
})
}
handleItemSelect(item, index) {
this.state.setData[index] = "green";
this.setState({
setData: this.state.setData
})
}
renderItem = ({ item, index }) => {
return (
<ListItem button={true}
onPress={() => this.handleItemSelect(item, index)} style={{ marginLeft: 0 }}>
<Body>
<Text style={{ color: this.state.setData[index] }}>{item.name}</Text>
</Body>
</ListItem>
);
};
render() {
return (
<FlatList style={{ marginTop: 30 }}
data={this.state.data}
renderItem={this.renderItem}
/>
);
}
}
You can set a color to your ListItem initially and then you can change the color on a click event.
Hope this helps!
I created a checkbox inside my flatlist but when I click on the checkbox all the check boxes will render. I want to render the checkbox I press not the entire checkboxes.
This is my code:
const list = [
{
name: 'Kevin',
id:0
},
{
name: 'John',
id:1
},
]
export default class TestingScreen extends Component {
constructor(props) {
super(props)
this.state = {
checked: false
}
}
handleCheckBox = () => this.setState({ checked: !this.state.checked })
renderItem = ({ item }) => (
<ListItem
title={<View><Text>{item.name}</Text></View>}r
leftAvatar={
<View>
<Image
style={{width:50, height:50,borderRadius:25 }}
source={require('../Components/Assets/oval.png')} />
</View>
}
rightAvatar={
<CheckBox
checked={this.state.checked}
onPress={() => this.setState({checked: !this.state.checked})}
/>
}
>
</ListItem>
)
render() {
return(
<View style={styles.container}>
<FlatList
keyExtractor={this.keyExtractor}
data={list}
renderItem={this.renderItem}
extraData={this.state}
/>
</View>
)
};
}
Is there a way I could just render the checkbox I pressed?
Any comments or advise would be really helpful thanks! :)
You are using the same state variable for all your checkboxes. Clicking on one of the checkboxs will update the single global variable checked you gave to all checkboxes.
You have to either create a checkBox component with his own state or update a variable inside the list you are using to render the different items.
For example:
1)Move your list variable inside the state adding a checked key:
const list = [
{
name: 'Kevin',
id:0,
checked:false
},
{
name: 'John',
id:1,
checked:false
},
]
this.state = {
list: list
}
2) Use the state into the flatList data:
<FlatList
keyExtractor={this.keyExtractor}
data={this.state.list}
renderItem={this.renderItem}
extraData={this.state}
/>
3) Change the onPress of the checkBox:
<CheckBox
checked={this.state.list[item.id].checked}
onPress={() => this.checkThisBox(item.id)}
/>
where:
checkThisBox=(itemID)=>{
let list=this.state.list
list[itemID].checked=!list[itemID].checked
this.setState({list:list})
}
save the item.id in the state when triggering onPress
Example :
this.state = {
checked : null
}
then in your checkbox :
<CheckBox
checked={this.state.checked === item.id}
onPress={() => this.setState({checked: item.id})}
/>
OR
you can do it like
this.state = {
list: list,
},
then in your flatlist
<FlatList
keyExtractor={this.keyExtractor}
data={this.state.list}
renderItem={({item, index}) =>
<ListItem>
<Left>
<Text>{item.name}</Text>
</Left>
<Right>
<CheckBox
checked={item.checked}
onPress={() => this.onCheck(item,index)}
/>
</Right>
</ListItem>
}
extraData={this.state}
/>
oncheck function
onCheck = (item, i) => {
let items = this.state.list;
items[i].checked = items[i].checked ? ! items[i].checked : true
this.setState({list:items})
}
I want to make hide and show each view which have {param.body} in the list when I onPress the each TouchableOpacity. But all of my list view is hiding and showing when I onPress only one. How can I make only one view hide and show?
I've got the key value in each View.
this is my code
const DOWN_ARROW = require('../assets/images/ic_arr_down.png');
const UP_ARROW = require('../assets/images/ic_arr_up.png');
export default class Schedule extends Component{
constructor(props){
super(props);
this.state = {showbody : true, activeImage : true}
}
toggleBody = () =>{
this.setState({showbody : !this.state.showbody, activeImage : !this.state.activeImage,})
}
data = {
contents: [
{
date: '4/3',
money: '$15000',
body: 'this is component 1',
},
{
date: '4/4',
money: '$200000',
body: 'this is component 2',
},
]
}
render() {
let arrowImage = this.state.activeImage ? DOWN_ARROW : UP_ARROW;
return(
<View style = {styles.container}>
<ScrollView style={{alignSelf: 'stretch'}}>
{
this.data.contents ? this.data.contents.map((param, i) => {
return(
<View>
<View style={styles.dropdown} key={i}>
<Text style={{fontSize: 16}}>{param.date}</Text>
<TouchableOpacity style={{backgroundColor: 'blue'}} onPress={this.toggleBody}>
<Image source={arrowImage}/>
</TouchableOpacity>
</View>
{this.state.showbody ? (<View><Text>{param.body}</Text></View>) : null}
</View>
);
})
: null
}
</ScrollView>
</View>
)
}
}
I expect when I press image on view which key is 1, the view right below that contains {param.body} do hide and show. but all of the view is hide and showing. :(
This is most common type of problem most beginners face while start to work in React/React Native.You got to do like this.
1.Maintain a variable activeItem in state which by the name is holding the current active item.
2.Update this variable onPress inside the loop like this.
updateActiveItem=itemIndex=>{
this.setState({activeItem:itemIndex})
}
....
....
render(){
return(
<View>
{
someArrayofItems.map((item,index)=>{
return(
<View>
<Touchable onPress={()=>this.updateActiveItem(index)}>
...
</Touchable>
{this.state.activeItem===index &&(SOME VIEW YOU WANT TO SHOW
CONDITIONALLY)}
</View>
)
})
}
</View>
)
}
Follow this steps:-
1. Take one variable temp which will be empty on first time
2. Now on click of tochableopacity store that particluar item key in variable.
3. The condition of show view will like this
this.state.temp == param.keyvalue ? "show your view" : null
So it will work easily.
I suggested to use flatlist instead of this.
In this module I am trying to create a survey module similar to the one in twitter.
at first, color of text input borders are grey and when I focus (click) the text input, only one of them (clicked one) must be blue. Same idea when I type a text, they all shouldn't get the same value. I should be able to get each text input value that I created by clicking plus icon, as a String
Should I use a flatlist or a listview rather than a for loop ?
React-Native Listview, press row and change that row style
I also tried to solve it according to this example.
I change this example a little bit, I was able to change border color of clicked one. but still, I couldn't get the values...
Any solution suggestion ? Thank you.
screenshot 1
screenshot 2
This is my code;
changeInputBorderColor = () => {
const newinputBorderColor = cloneDeep(this.state.inputBorderColor);
newinputBorderColor.bar = '#04A5F5';
this.setState({inputBorderColor: {bar: newinputBorderColor.bar}});
};
changeInputBorderColor2 = () => {
this.setState({
inputBorderColor: {
bar: 'grey'
}
})
};
incrementInputCount = () => {
if (this.state.inputCounter < 5) {
this.setState(prevState => {
return {inputCounter: prevState.inputCounter + 1}
});
console.log(this.state.inputCounter);
}
else {
this.setState(prevState => {
return {inputCounter: prevState.inputCounter}
});
alert("Maximum soru sayısına ulaştınız");
}
};
render() {
let surveyOptions = [];
for (let i = 0; i < this.state.inputCounter; i++) {
console.log(this.state.inputCounter);
surveyOptions.push(
<View key={i}>
<View>
<TextInput
style={[styles._surveyTextInput, {borderColor: this.state.inputBorderColor.bar}]}
onChangeText={(text) => this.setState({text})}
value={this.state.text}
onFocus={this.changeInputBorderColor}
onBlur={this.changeInputBorderColor2}
placeholder={"Secenek " + (i + 1)}
/>
</View>
</View>
)
}
return (
<View style={styles._surveyMainContainer}>
<View style={{flex: 0.8}}>
{surveyOptions}
<TouchableOpacity style={{position: 'absolute', right: 5, top: 5}}>
<Ionicons name={"ios-close-circle"}
size={30}
color={'black'}
/>
</TouchableOpacity>
<TouchableOpacity style={{position: 'absolute', right: 5, top: 45}}
onPress={this.incrementInputCount}>
<Ionicons name={"ios-add-circle"}
size={30}
color={'blue'}
/>
</TouchableOpacity>
</View>
<View style={{flex: 0.2}}>
<View
style={styles.renderSeparator}
/>
<Text style={{fontWeight: 'bold', margin: 5}}>Anket süresi</Text>
</View>
</View>
);
}
You can do it with a .map however you have to set it up correctly so that each TextInput has its own value in state. Currently what you are doing is setting the same value in state for each TextInput this results in every TextInput having the same value. Clearly not what you want.
Create an initial array in state (textArray) that has all values as empty strings, this will be used to store the values from each TextInput.
Set the focusedIndex to be null in state
Create a function that uses the previous state value to update the current state.
Create a function to handle the changing of the box color, it will just compare the TextInput index with the current focusedIndex
Iterate over the textArray and create the TextInput components. Make sure each TextInput has its own value in state.
Make sure we set the value of the focusedIndex in the onFocus and onBlur in the TextInput. When it blurs we should set the value to null so that it removes the border color when the keyboard is dismissed.
So we could do something like the following
export default class App extends React.Component {
constructor(props) {
super(props);
// construct an array with the number of textInputs we require,
// each value an empty string
// set this array in state
// set the focusedIndex to null
let textArray = Array(6).fill('');
this.state = {
textArray: textArray,
focusedIndex: null
}
}
// this function will handle setting of the state when each TextInput changes
onChangeText = (text, index) => {
// as there are going to be a lot of setState calls
// we need access the prevState before we set the next state.
this.setState(prevState => {
prevState.textArray[index] = text
return {
textArray: prevState.textArray
}
}, () => console.log(this.state.textArray))
}
// handle the border color
handleBorderColor = (index) => {
return index === this.state.focusedIndex ? 'red' : 'grey'
}
render() {
// here we map the items in the `this.state.textArray`
// notice that each TextInput is give a specific value in state
// that will stop the overlap
return (
<View style={styles.container}>
{this.state.textArray.map((text, index) => {
return <TextInput
style={{height: 40, marginVertical: 10, borderColor: this.handleBorderColor(index), borderWidth: 1}}
onChangeText={text => this.onChangeText(text, index)}
value={this.state.textArray[index]}
placeholder={`placeholder for ${index}`}
onFocus={() => this.setState({focusedIndex: index})}
onBlur={() => this.setState({focusedIndex: null})}
/>
})}
</View>
);
}
}
If you then want to access a specific value for a TextInput you can do so like this
let value = this.state.textArray[index]; // where the index is the value you want
Here is an example snack showing the code working
https://snack.expo.io/#andypandy/map-multiple-textinputs
It is definitely worthwhile looking at the following articles about state, as I have used these properties in this example.
https://medium.learnreact.com/setstate-is-asynchronous-52ead919a3f0
https://medium.learnreact.com/setstate-takes-a-callback-1f71ad5d2296
https://medium.learnreact.com/setstate-takes-a-function-56eb940f84b6
I have a list of objects in an array (tagList). When clicked, the object is pushed into another array (selectedTags).
var selectedTags = [];
export default class RegisterTags extends Component {
constructor(props) {
super(props)
this.state = {
tagList: [],
}
}
insertTag = (tag) =>{
selectedTags.push(tag)
}
render() {
var tagsToRender = this.state.tagList.map((tag) => {
return(
<TouchableOpacity key={tag.id} onPress = {() => {this.insertTag(tag) }}>
<View style={styles.tagStyle}>
<Text style={styles.tagText}>{tag.name}</Text>
</View>
</TouchableOpacity>
)
})
return(
<View style={styles.container}>
<ScrollView contentContainerStyle={styles.ScrollViewContainer}>
{tagsToRender}
</ScrollView>
</View>
)
}
}
I would like to change the style of the View for the tag that is present in the selectedTags dynamically. I thought of using lodash's _.includes(selectedTags, tag) to check if the tag is included in the selectedTags, but this does not work. How can I change the style dynamically for the selected tags when the tag is pressed?
simply check if tag exist in selected tags and assign the selecedTag style :
var tagsToRender = this.state.tagList.map((tag) => {
var selected = selectedTags.indexOf(tag) > -1
var style = selected ? styles.selectedTagStyle : styles.tagStyle
return(
<TouchableOpacity key={tag.id} onPress = {() => {this.insertTag(tag) }}>
<View style={style}>
<Text style={styles.tagText}>{tag.name}</Text>
</View>
</TouchableOpacity>
)
})