Set state for element in array [react-native] - react-native

I have a view
renderFrame = () => {
const frames = []
for(let i=1; i<=10; i++) {
frames.push (
<TouchableOpacity style={this.state.isActive ? styles.frameItemActive : styles.frameItem} key={i} onPress={this.handleChoiceTime.bind(this, i)}>
<Text style={{color: '#8E8E93', fontSize: 17}}>{i}</Text>
</TouchableOpacity>
)
}
return frames
}
how to set state for element in frame, default is false, when click then is true, double click then is false

Try the below code.
I have kept the frames outside the render method and used flatlist. Now When the button is pressed the isFrameActive flag of that particular frame element will change and re-render. So this should work as per your requirement. Give a try!
this.state = {
frames : [
{
id: 1,
value: 1,
isFrameActive: true
},
{
id: 2,
value: 2,
isFrameActive: true
},
{
id: 3,
value: 3,
isFrameActive: true
}
]
};
handleChoiceTime = (index, item) => {
this.state.frames[index].isActive = !this.state.frames[index].isActive;
this.setState({
frames: this.state.frames
})
}
renderFrame = (item) => {
<TouchableOpacity style={item.isFrameActive ? styles.frameItemActive : styles.frameItem} key={item.id} onPress={this.handleChoiceTime.bind(index, item)}>
<Text style={{color: '#8E8E93', fontSize: 17}}>{item.value}</Text>
</TouchableOpacity>
}
render() {
<Flatlist
data={this.state.frames}
renderItem = {(item) => this.renderFrame(index, item)}
extraData={this.state}
/>
};

Related

React-Native : useState to update object inside array

I have a list of items, when one is clicked, it navigates to a modal which displays a list of options.
I am trying to increment the counter inside each option, it works as intended BUT when I exit the modal screen and go back to it, the options counter are not reseted.
const myOptions = [
{ id: '001', name: 'option 001', counter: 0 },
{ id: '002', name: 'option 002', counter: 0 },
];
function ModalScreen({ route, navigation }) {
const [options, setOptions] = useState(myOptions);
let tempArr = [...myOptions];
// Array where I increment the counter, before passing it to setOptions(tempArr)
useEffect(() => {
return () => {
// because of let tempArr = [...myOptions]; changes in tempArr are copied
in myOptions. I want to reset myOptions when I exit the component
console.log(options);
console.log(myOptions) // both output are identical
};
}, []);
return (
<View>
<Text style={{ fontWeight: 'bold', marginBottom: 15 }}>
Click on an option to increment counter by 1
</Text>
<FlatList
keyExtractor={item => item.name}
extraData={tempArr}
data={options}
renderItem={({ item, index }) => (
<TouchableOpacity
onPress={() => {
tempArr[index].counter++;
setOptions(tempArr);
}}>
<Text>
{item.name} - counter: {item.counter}
</Text>
</TouchableOpacity>
)}
/>
</View>
);
}
I did a demo here :
https://snack.expo.io/#oliviermtl/carefree-marshmallows
I spent my day trying to figure out this one... Let me know if something needs more explanation
Thanks
Change
useEffect(() => {
return () => {
console.log(options);
console.log(myOptions)
};
}, []);
to
useEffect(() => {
return () => {
myOptions[0].quantity = 0;
myOptions[1].quantity = 0;
};
},[]);
What I was doing is that changing quantity values to 0, whenever user closes or comes back out from modal.
Hope this helps!

How to refresh/re-render flatlist on react-native?

im trying to refresh my flatlist from some page without going back to the principal menu, but it doesnt work.
I've already readed about extraData, but it doesnt work either.
Basiclly my program is like that:
I have a page called "passwords" and i add some passwords there from another page called "add passwords". When i click to add a password, i want to refresh the flatlist from the page "passwords" to show me the password that i just added.
This is my code from the page "add passwords"
...
state = {
arr: [],
local: '',
password: '',
obj: {
local: '',
password: ''
},
count: 1,
texto: ''
};
componentDidMount() {
//Here is the Trick
const { navigation } = this.props;
//Adding an event listner om focus
//So whenever the screen will have focus it will set the state to zero
this.focusListener = navigation.addListener('didFocus', () => {
this.setState({ count: 0 });
});
}
storeItem(item) {
try {
//we want to wait for the Promise returned by AsyncStorage.setItem()
//to be resolved to the actual value before returning the value~
console.log(item)
var joined = this.state.arr.concat(item);
console.log(joined)
this.setState({ arr: joined })
AsyncStorage.setItem('array', JSON.stringify(joined));
console.log(this.state.arr)
} catch (error) {
console.log(error.message);
}
}
componentWillMount() {
AsyncStorage.getItem('array').then(array => {
item = JSON.parse(array)
array ? this.setState({ arr: item }) : null;
console.log(item)
})
}
render() {
return (
<View style={styles.container}>
<TextInput
style={{ height: 40, borderColor: 'gray', borderWidth: 1 }}
onChangeText={(text) => this.setState({ local: text })}
value={this.state.local}
/>
<TextInput
secureTextEntry={true}
style={{ height: 40, borderColor: 'gray', borderWidth: 1 }}
onChangeText={(text) => this.setState({ password: text })}
value={this.state.password}
/>
<Button title='Adicionar'
onPress={() => this.storeItem({ local: this.state.local, password: this.state.password }) + alert("Adicionado com sucesso!") + this.props.navigation.navigate('Passwords')}
></Button>
</View>
);
}
}
And this is my page "passwords" where i want to refresh
componentWillMount() {
const { navigation } = this.props;
this.willFocusListener = navigation.addListener(
'willFocus',
() => {
this.setState({ count: 10 })
}
)
AsyncStorage.getItem('array').then(array => {
item = JSON.parse(array)
item ? this.setState({ arr: item }) : null;
console.log(this.state.arr)
})
}
renderItem = ({ item }) => (
<View style={{ flexDirection: 'row' }} style={styles.passwordContainer}>
<Text> {item.local} </Text>
<Text> {item.password} </Text>
</View>
)
render() {
return (
<View style={styles.container}>
<FlatList
data={this.state.arr}
renderItem={this.renderItem}
extraData={this.state} //this is what i tryied
/>
</View>
);
You can use your listener to update the state.
componentWillMount() {
this.willFocusListener = navigation.addListener(
'willFocus',
() => this.updateData()
}
updateData = () => {
this.setState({ count: 10 });
AsyncStorage.getItem('array').then(array => {
item = JSON.parse(array)
item ? this.setState({ arr: item }) : null;
console.log(this.state.arr)
});
}
Any state changes will rerender items.

react-native changes the properties of the elements in the array?

I have a FlatList and I want to implement a radio button.My idea is to change the selected property of an element in this.state.data to control it,but I am a newbie, I don't know how to change the property of an element in this.state.data.
Here is my code:
this.state = {
data: [
{
month:1,
price:18,
selected:true
},
{
month:3,
price:48,
selected:false
},
{
month:12,
price:128,
selected:false
},
],
};
<FlatList
data={this.state.data}
renderItem={({item, index, separators}) => (
<TouchableOpacity onPress={() => this.radio(index,item)}>
<View style={item.selected ? {borderWidth:3,borderColor:'#FFA371',borderRadius:15}:{}}>
<View style={styles.itemDefalut}>
<View style={{ flexDirection: "column", flex: 1 }}>
<Text>
Months
</Text>
<Text>{item.month} Months</Text>
</View>
<View>
<Text>${item.price}</Text>
</View>
</View>
</View>
</TouchableOpacity>
)}
/>
radio(index,item) {
for (var variable in this.state.data) {
variable.selected = false;
}
item.selected = true;
}
first pass only index from onpress
onPress={() => this.radio(index)
then in radio function do something like this
radio = index => {
let data = [ ...this.state.data ];
this.state.data.map((elem,key)=>{
if(elem.month==data[index].month){
data[key]={...data[key], selected: true};
}else{
data[key]={...data[key], selected: false};
}
})
this.setState({ data:data});
}
radio(item) {
let data = [...this.state.data];
let index = data.findIndex(el => el.month === item.month);
data[index] = {...data[index], selected: !item.selected};
this.setState({ data });
}
In TouchableOpacity on press it should be
<TouchableOpacity onPress = {this.radio.bind(this,item)}>

React Native:How to change dynamic view borderColor colour?

I have created 5 Text programatically in react native .
I want to change the borderColor colour of view By click item.
I tried using below code .But its changing all 5 views borderColor colour .
I want change borderColor colour of only one view.
for (var i = 0; i < 4; i++) {
pills.push (this.renderPill (i));
}
renderPill (index) {
return (
<TouchableOpacity key={index} style={ this.state.status ? boxStyle : boxStyleSelected } onPress={this.itemClick.bind(this)}>
<View >
<Text>
{index}
</Text>
</View>
</TouchableOpacity>
);
}
}
multiItemClick (index) {
this.setState({ status: true });
}
boxStyle: {
borderColor: '#ffffff',
},
boxStyleSelected: {
borderColor: '#000000',
}
You should use FlatList to render your item or component list.
Here are an Example:
render() {
return (
<View style={styles.container}>
<FlatList
data={ this.state.listItem }
ItemSeparatorComponent = {this._itemSeparator}
keyExtractor={(item, index) => index}
renderItem={this._renderItem}
selected={this.state.selected}
/>
</View>
);}
Separate the item list
_itemSeparator = () => (<View style={{ height: 1, backgroundColor: "#607D8B" }}/>);
Render the Item list
_renderItem = (item) => {
return (
<TouchableOpacity
key={item.index}
style={this.state.selected == item.index ? styles.boxSelected : styles.boxStyle}
onPress={()=>this._itemClick(item)}
>
<View style={{alignItems:'center'}}>
<Text>
{item.item.key}
</Text>
</View>
</TouchableOpacity>
);}
Event on Click item to Change the Style
_itemClick(item) { this.setState({selected:item.index}) }
Data State
constructor(props){
super(props);
this.state = {
selected: null,
listItem: [
{key: 'One'},
{key: 'Two'},
{key: 'Three'},
{key: 'Four'},
{key: 'Five'},
{key: 'Six'},
{key: 'Seven'},
{key: 'Eight'},
{key: 'Nine'},
{key: 'Ten'},
{key: 'Eleven'},
{key: 'Twelve'}
]
};}
Style Sheet your item
const styles = StyleSheet.create({
container :{
justifyContent: 'center',
flex:1,
margin: 10,
paddingTop:50
},
boxStyle: {
backgroundColor: '#778788',
padding: 10,
margin:3
},
boxSelected: {
backgroundColor: 'red',
padding: 10,
margin:3
}
});
enter image description here
This is what i got for you, Hope it will save your time.
The problem is, that you are using the state for all views. Try to modify the code to something, that depends on the index you clicked.
for (var i = 0; i < 4; i++) {
pills.push (this.renderPill (i));
}
renderPill (index) {
return (
<TouchableOpacity key={index} style={ this.state.status[index] ? boxStyle : boxStyleSelected } onPress={(index) => this.itemClick(index)}>
<View >
<Text>
{index}
</Text>
</View>
</TouchableOpacity>
);
itemClick(index) {
let status = this.state;
status[index] = true;
this.setState({
status: status
})
I have not tested this, so I am not 100% sure about the syntax, but i think the idea is clear. You have to save wich item is clicked in an array, so you know what item has to be rendered with the different border.
But I also would really advice to use a FlatList here https://facebook.github.io/react-native/docs/flatlist.html Because what you are trying to do is render a clickable List. That will give you way more control over the items, their direct styling and the list container. Also you would not need to have two separate lists, but could use an array of objects like this
{
value: 1
clicked: true
}
which makes it more readable and maintainable.
edit: there is actually a code sample for a multiselect on the link provided, i adapted it to your pills. Just hand the array to the component from somewhere else. Id and title is needed. View looks like this:
Code goes like this:
export default class MyView extends React.Component {
constructor(props) {
super(props);
this.state = {
pills: [{id: 1, title: 'Pill 1'},
{id: 2, title: 'Pill 2'},
{id: 3, title: 'Pill 3'}],
};
}
render() {
const {pills} = this.state;
return (
<MultiSelectList data={pills}/>
);
}
}
class MultiSelectList extends React.PureComponent {
state = {selected: (new Map())};
_keyExtractor = (item, index) => item.id;
_onPressItem = (id) => {
// updater functions are preferred for transactional updates
this.setState((state) => {
// copy the map rather than modifying state.
const selected = new Map(state.selected);
selected.set(id, !selected.get(id)); // toggle
return {selected};
});
};
_renderItem = ({item}) => (
<MyListItem
id={item.id}
onPressItem={this._onPressItem}
selected={!!this.state.selected.get(item.id)}
title={item.title}
/>);
render() {
return (
<FlatList
data={this.props.data}
extraData={this.state}
keyExtractor={this._keyExtractor}
renderItem={this._renderItem}
/>
);
}
}
class MyListItem extends React.PureComponent {
_onPress = () => {
this.props.onPressItem(this.props.id);
};
render() {
const color = this.props.selected ? "red" : "black";
return (
<TouchableOpacity onPress={this._onPress}>
<View>
<Text style={{borderStyle: 'solid', borderWidth: 2, borderColor: color }}>
{this.props.title}
</Text>
</View>
</TouchableOpacity>
);
}
}

React Native onPress active state change image source on Carousel

Trying to change image of a button on my carousel elements, currently it (below code) changes all the images when I click any of them. I'd like change that only current carousel's image. Any ideas? Thanks
class CarouselImages extends React.Component {
constructor(props) {
super(props);
this.state = {
myImagesArray: [
{
key: 1,
title: 'Category'
},
{
key: 2,
title: 'Category'
},
{
key: 3,
title: 'Category'
}
],
icon_active: false,
}
activateCarouselButton = a => {
const newState = Object.assign(
{},
{
icon_active: false,
},
{ [a]: true },
)
this.setState(newState);
}
}
render = () => {
const { icon_active } = this.state;
var myCarousel = this.state.myImagesArray.map(function (index) {
return (
<View key={index}>
<TouchableHighlight onPress={() => activateCarouselButton('icon_active')} >
<Image
source={icon_active ? require('../Image/active#2x.png') : require('../Image/disabled#2x.png')} />
</TouchableHighlight>
</View>
);
});
return (
<View>
<Carousel
style={{ backgroundColor: '#fff' }}>
{myCarousel}
</Carousel>
</View>
)
}
}
You need to hold key of the icon in the icon_active state, not a boolean. This gives you a hunch on how to do it:
render() {
const { icon_active } = this.state;
return (
this.state.myImagesArray.map((image) => {
return (
<View key={image.key}>
<TouchableHighlight onPress={() => activateCarouselButton(image.key)}>
<Image source={icon_active === image.key ? require('../Image/active#2x.png') : require('../Image/disabled#2x.png')} />
</TouchableHighlight>
</View>
)
})
)
}
<Image source={this.props.secureTextEntry ?
require('../../assets/images/signup/Showpassword.png') :
require('../../assets/images/signup/Hidepassword.png')} />