I have a array of data which I render in flatlist in React-Native. On pressing one of the items I want it to fade away, but instead all items on the flatlist get animated and not just the one that I pressed on.
constructor(props){
super(props);
this.state = { fadeAnim: new Animated.Value(1) }
onPressButtonnotdelivered(item) {
//Alert.alert(item.name)
const animations = [
Animated.timing(this.state.fadeAnim, {
toValue: 0,
duration: 500
}),
];
Animated.sequence(animations).start()
}
render() {
return (
<View>
<FlatList
data={this.state.data}
extraData={this.state}
keyExtractor={item => item.id}
renderItem={({ item, index }) => {
return (
<Animated.View key={index} style={[styles.animatedview, {opacity: this.state.fadeAnim}]}>
<View>
<View style={[styles.button, {backgroundColor: '#E94F64'}]}>
<TouchableOpacity style={styles.buttonview}
onPress={() => {this.onPressButtonnotdelivered(item)}}>
<View style={styles.btnIcon}>
<Icon name="block" size={30} />
<Text>Not delivered</Text>
</View>
</TouchableOpacity>
</View>
</View>
</Animated.View>
);
}}
/>
</View>
);
}
You need to add one more state to your component say indexToAnimate and set it to null.
Then put one condition in style of <Animated.View>
<Animated.View
key={index}
style={[
styles.animatedview,
{
opacity:
index == this.state.indexToAnimate
? this.state.fadeAnim
: "whatever your static value is..(in integer)"
}
]}
/>
and set indexToAnimate state on onPress method of <TouchableOpacity>
<TouchableOpacity
style={styles.buttonview}
onPress={() => {
this.setState({ indexToAnimate: index }, () => this.onPressButtonnotdelivered(item));
}}
>
<View style={styles.btnIcon}>
<Icon name="block" size={30} />
<Text>Not delivered</Text>
</View>
</TouchableOpacity>
What if you add a conditional render to the renderItem like:
renderItem={({ item, index }) => {
if (index === 'id')
return(<Animated.View> ... </Animated.View>);
else
return(<View> ... </View>);
}
Related
I have a screen with card views.
Each card view has:
1x picture
1x title
1x description
1x touchable opacity
I was hoping to figure out a way that each touchable opacity has different navigation.
Item 0 will have navigation to screen x, the item 1 will have navigation to screen y.
My doubt is it possible to have different functions for each touchable opacity ?
function ServiceCoverageButtong() {
const navigation = useNavigation();
return (
<View>
<TouchableOpacity
style={styles.button}
onPress={() => navigation.navigate('GeneralInformationSupportScreen')}>
<Text style={styles.buttonText}>Teste</Text>
</TouchableOpacity>
</View>
);
}
const CardItemNewsProvider = ({item, index}) => {
return (
<View style={styles.container} key={index}>
<Image source={item.imgUrl} style={styles.image} />
<Text style={styles.header}>{item.title}</Text>
<Text style={styles.body}>{item.body}</Text>
<ServiceCoverageButtong />
</View>
);
};
How can I create several functions and use the item of CardItemNewsProvider?
I am new to React Native and I am struggling with doing that.
Thanks :)
Yes it's possible. You can pass a prop to your <ServiceCoverageButtong state={"0"}/>
And in your ServiceCoverageButtong() get the state from your props and run a check on what should be returned.
function ServiceCoverageButtong({state}) {
const navigation = useNavigation();
if (state == "0") {
return (
<View>
<TouchableOpacity
style={styles.button}
onPress={() => navigation.navigate('GeneralInformationSupportScreen')}>
<Text style={styles.buttonText}>Teste</Text>
</TouchableOpacity>
</View>
);
}
} else {
return (
<View>
<TouchableOpacity
style={styles.button}
onPress={() => navigation.navigate('anotherScreen')}>
<Text style={styles.buttonText}>Teste</Text>
</TouchableOpacity>
</View>
);
}
}
If you use one component for your buttons, you can just add onPress prop to your DataNewsProvider
let DataNewsProvider = [
{
title: NewsHomeCountryTitle,
body: NewsHomeCountryBody,
imgUrl: Images.newsYourCountryImage,
textButton: NewsTextButton,
onPress: () => navigation.navigate('GeneralInformationSupportScreen'),
},
{
title: NewsWorldwideTitle,
body: NewsWorldwideBody,
imgUrl: Images.newsWorldwideImage,
textButton: NewsTextButton,
onPress: () => navigation.navigate('anotherScreen'),
},
];
And pass it to your button components TouchableOpacity
const CardItemNewsProvider = ({item, index}) => {
return (
<View style={styles.container} key={index}>
<Image source={item.imgUrl} style={styles.image} />
<Text style={styles.header}>{item.title}</Text>
<Text style={styles.body}>{item.body}</Text>
<ServiceCoverageButtong state={item.stateButton} onPress={item.onPress}/>
</View>
);
};
This way you don't need to have additional conditions, and you just pass those functions as it is.
Thanks caslawter!
For anyone interested.
function ServiceCoverageButtong({state}) {
const navigation = useNavigation();
if (state === '0') {
console.log('state', state);
return (
<View>
<TouchableOpacity
style={styles.button}
onPress={() =>
navigation.navigate('GeneralInformationSupportScreen')
}>
<Text style={styles.buttonText}>Hi I'm a test</Text>
</TouchableOpacity>
</View>
);
} else {
console.log('state', state);
return (
<View>
<TouchableOpacity
style={styles.button}
onPress={() => navigation.navigate('anotherScreen')}>
<Text style={styles.buttonText}>You're really into testing</Text>
</TouchableOpacity>
</View>
);
}
}
const CardItemNewsProvider = ({item, index}) => {
return (
<View style={styles.container} key={index}>
<Image source={item.imgUrl} style={styles.image} />
<Text style={styles.header}>{item.title}</Text>
<Text style={styles.body}>{item.body}</Text>
<ServiceCoverageButtong state={item.stateButton} />
</View>
);
};
And another snipet:
let DataNewsProvider = [
{
title: NewsHomeCountryTitle,
body: NewsHomeCountryBody,
imgUrl: Images.newsYourCountryImage,
textButton: NewsTextButton,
stateButton: '0',
},
{
title: NewsWorldwideTitle,
body: NewsWorldwideBody,
imgUrl: Images.newsWorldwideImage,
textButton: NewsTextButton,
stateButton: '1',
},
];
I am using React Native FlatList and React Native Modal.
Upon clicking on the item from the FlatList, I want to view 1 Modal only (containing the details of the item selected).
However, if there are 4 items in the FlatList, selecting 1 item causes
all 4 modals to pop up.
Is there anyway I can display only 1 modal for 1 selected item in the FlatList instead of multiple modal?
Code Snippet below (some lines of code were removed as it's not needed):
constructor(props) {
super(props);
this.state = {
dataSource: [],
isLoading: true,
modalVisible: false,
}
}
setModalVisible = (visible) => {
this.setState({ modalVisible: visible });
}
viewModal(item, price) {
const { modalVisible } = this.state;
return (
<Modal
statusBarTranslucent={true}
animationType={"slide"}
transparent={true}
visible={modalVisible}
onRequestClose={() => {
Alert.alert("Modal has been closed.");
}}
>
<View>
<View>
<View>
<Text>
Appointment Start Time:
</Text>
<Text>
{moment(item.time_start).format('h:mm a')}
</Text>
</View>
<View>
<Text>
Appointment End Time:
</Text>
<Text>
{moment(item.end_start).format('h:mm a')}
</Text>
</View>
<View style={styles.row}>
<Text>
Total:
</Text>
<Text>
{price}
</Text>
</View>
<View>
<View>
<Button
mode="outlined"
onPress={() => {
this.setModalVisible(!modalVisible);
}}
>
{'Cancel'}
</Button>
</View>
<View>
<Button
mode="contained"
onPress={() => {
this.setModalVisible(!modalVisible);
}}
>
{'Accept'}
</Button>
</View>
</View>
</View>
</View>
</Modal>
);
}
viewFreelancerTime() {
return (
<View>
<FlatList
renderItem={({ item }) => {
let totalPrice = (parseFloat(item.service_price) + parseFloat(item.service_deposit)).toFixed(2);
return (
<Container>
{this.viewModal(item, totalPrice)}
<TouchableNativeFeedback
onPress={() => {
this.setModalVisible(true);
}}
>
<View>
<View>
<Text>
{moment(item.time_start).format('h:mm a')}
</Text>
</View>
<View>
<Text>
{totalPrice}
</Text>
</View>
</View>
</TouchableNativeFeedback>
</Container>
);
}}
/>
</View>
);
}
render() {
return (
<>
<View style={{ flex: 1 }}>
{this.viewFreelancerTime()}
</View>
</>
);
};
The poblem is that you are rendering the modal in the renderItem method, so every time you select an item, the modal will open in each rendered item.
To solve that you will have to render a custom Modal component with an absolute position at the same level of your FlatList, and pass the selected item information as props.
UPDATE
Just something like this:
import React, {useState} from "react";
import { Modal } from "react-native";
export default function MyFlatList(props) {
const [selectedItem, setSelectedItem] = useState(null);
const handleOnSelectItem = (item) => {
setSelectedItem(item);
};
const handleOnCloseModal = () => {
setSelectedItem(null);
};
renderItem = ({ item }) => {
return (
<Container>
<TouchableNativeFeedback onPress={() => handleOnSelectItem(item)}>
<View>
<View>
<Text>{moment(item.time_start).format("h:mm a")}</Text>
</View>
<View>
<Text>{totalPrice}</Text>
</View>
</View>
</TouchableNativeFeedback>
</Container>
);
};
return (
<View>
<FlatList renderItem={this.renderItem} />
<CustomModal isVisible={selectedItem} selectedItem={selectedItem} onClose={handleOnCloseModal} />
</View>
);
}
export function CustomModal(props) {
const { isVisible, item, onClose, /*...*/ } = props;
// Play with the item data
let totalPrice = (
parseFloat(item.servicePrice) + parseFloat(item.serviceDeposit)
).toFixed(2);
return <Modal visible={isVisible} onRequestClose={onClose}>{/*...*/}</Modal>; // Render things inside the data
}
I suggest you to do a pagination and play with FlatList native props if you are going to implement an infinite scroll.
Pd: to reduce re-renders because of state updates, I am reusing the selectedItem state, so if it is not null then the modal will be visible
I'm still new in react native and programming, and i am trying to pass items from my flat list into a modal. What i'm about to pass is the icon, status, and description. How am i supposed to do that?
this is my flatlist
buildPanel(index, item) {
let panel = [];
let keys = DBkeys['Requests'].MyRequest;
let status = item[keys['status']];
panel.push(<View style={{ position: 'absolute', right: 0, bottom: 0, padding: normalize(5), alignItems: 'center' }} key={'status'}>
<TouchableOpacity onPress={this.handleShowModal}>
<Icon name={img.itemStatus[status].name} type={img.itemStatus[status].type} color={img.itemStatus[status].color} size={normalize(38)} />
</TouchableOpacity>
</View>);
return panel;
}
<View style={[styles.panelContainer, status === 'success' ? {} : { backgroundColor: color.white }]}>
<FlatList
showsVerticalScrollIndicator={false}
progressViewOffset={-10}
refreshing={this.state.refreshing}
onRefresh={this.onRefresh.bind(this)}
onMomentumScrollEnd={(event) => event.nativeEvent.contentOffset.y === 0 ? this.onRefresh() : null}
data={content}
renderItem={({ item }) => item}
keyExtractor={(item, key) => key.toString()}
/>
</View>
<IconModal visible={this.state.modalVisible} close={this.handleDismissModal}/>
and this is my IconModal.js
const IconModal = (props) => {
return(
<Modal
isVisible={props.visible}
onBackdropPress={props.close}
>
<View style={styles.dialogBox}>
<View style={styles.icon}>
<Icon name='open-book' type='entypo' color='#ffb732' size={normalize(70)} />
</View>
<View style={styles.text}>
<Text style={styles.status}>Status</Text>
<Text>Desc</Text>
</View>
<TouchableOpacity onPress={props.close}>
<View>
<Text style={styles.buttonText}>GOT IT</Text>
</View>
</TouchableOpacity>
</View>
</Modal>
)
}
IconModal.propTypes ={
visible: PropTypes.bool.isRequired,
close: PropTypes.func,
}
from the renderItem of your FlatList,
You must be clicking somewhere to open modal,
when you click store that whole single item in state variable,
like, if you're using TouchableOpacity then
<TouchableOpacity onPress={this.passDataToModal}/>
...
...
passDataToModal=(item)=>{
this.setState({modalData:item},()=>{
//you can open modal here
});
}
and in your modal component,
you can pass data with prop.
<IconModal modalData={this.state.modalData} visible={this.state.modalVisible} close={this.handleDismissModal}/>
and you can use these data in IconModal as this.props.modalData.
If there is more data then you can always add another prop.
Define the following Hooks in your function Component.
const [modalVisible, setModalVisible] = useState(false);
const [modalData, setModalData] = useState([]);
const [modalTitle, setModalTitle] = useState('');
Now Trigger the function which opens the Modal, while simultaneously passing data into it.
<TouchableHighlight underlayColor="skyblue" onPress={() => { openSettingsModal(title,settings) } }>
Open Modal
</TouchableHighlight>
Here is the function code -
const openSettingsModal = (title,settings) => {
setModalTitle(title);
setModalData(settings);
setModalVisible(!modalVisible);
}
And finally a snippet of the Modal Code.
<Modal animationType="none" transparent={true} visible={modalVisible} >
<View style={styles.centeredView}>
<Text> { modalTitle }</Text>
<Text> { modalData }</Text>
</View>
</Modal>
For example:
class Container extends Component {
constructor(props) {
super(props)
this.state = {
modalVisible: false,
activeItemName: '', //state property to hold item name
activeItemId: null, //state property to hold item id
}
}
openModalWithItem(item) {
this.setState({
modalVisible: true,
activeItemName: item.name,
activeItemId: item.id
})
}
render() {
let buttonList = this.props.item.map(item => {
return (
<TouchableOpacity onPress={() => { this.openModalWithItem(item) }}>
<Text>{item.name}</Text>
</TouchableOpacity>
)
});
return (
<View>
{/* Example Modal Component */}
<Modal isOpen={this.state.openDeleteModal}
itemId={this.state.activeItemId}
itemName={this.state.activeItemName} />
{buttonList}
</View>
)
}
}
The goal is to pass the State of the Photos from my CameraRoll.js (Modal) to EventCreator.js(Modal) without using the React Redux. I'm using React Native Navigation V1.
I'm wondering maybe it is possible state of photos: [] become props? Just don't know how to do it. Need help, thank you guys!
Here are my codes:
CameraRoll.js:
state = {
photos: [],
index: null,
pickedImage: null
}
getPhotos = () => {
CameraRoll.getPhotos({
first: 200,
assetType: 'All'
})
.then(res => {
this.setState({
photos: res.edges,
});
})
.catch((err) => {
console.log('Error image: ' + err);
});
};
render() {
return(
<View style={styles.container}>
<Image source={{uri: this.state.pickedImage}} style={styles.image}/>
<ScrollView contentContainerStyle={styles.scrollView} showsVerticalScrollIndicator={false}>
{this.state.photos.map((photos, index) => {
return(
<TouchableHighlight
style={{opacity: index === this.state.index ? .5 : 1}}
onPress={() => this.setState({pickedImage: photos.node.image.uri})}
key={index}
underlayColor='transparent'
>
<Image
style={{width: width / 3, height: width /3}}
source={{uri: photos.node.image.uri}}
resizeMode='cover'
/>
</TouchableHighlight>
);
})}
</ScrollView>
</View>
);
}
EventCreator.js:
render(){
return(
<View style={styles.container}>
<EventInput
titleOnChangeText={this.eventNameChangedHandler}
descriptionOnChangeText={this.eventDescriptionChangedHandler}
titleEvent={this.state.controls.eventName}
descriptionEvent={this.state.controls.eventDescription}
/>
<Image
style={styles.image}
source={"I want to pass the image here from CameraRoll.js"}
resizeMode='contain'
/>
</View>
);
}
if you mean this:
onPress={() => this.setState({pickedImage: photos.node.image.uri})}
it just change the state value. What you should do is put an if statement on the return of cameraRoll.js:
private onPress = (img) => {
this.props.onImagePicked(img)
}
render() {
return(
<View style={styles.container}>
<Image source={{uri: this.state.pickedImage}} style={styles.image}/>
<ScrollView contentContainerStyle={styles.scrollView} showsVerticalScrollIndicator={false}>
{this.state.photos.map((photos, index) => {
return(
<TouchableHighlight
style={{opacity: index === this.state.index ? .5 : 1}}
onPress={() => this.onPress(photos.node.image.uri))}
key={index}
underlayColor='transparent'
>
<Image
style={{width: width / 3, height: width /3}}
source={{uri: photos.node.image.uri}}
resizeMode='cover'
/>
</TouchableHighlight>
);
})}
</ScrollView>
</View>
);
}
And in EventCreator.js:
constructor(){
super(props);
this.state = {
pickedImg: undefined
}
}
private onImagePicked = (newImg) => {
this.setState({
pickedImg: newImg
})
}
render(){
return(
<View style={styles.container}>
<EventInput
titleOnChangeText={this.eventNameChangedHandler}
descriptionOnChangeText={this.eventDescriptionChangedHandler}
titleEvent={this.state.controls.eventName}
descriptionEvent={this.state.controls.eventDescription}
/>
<Image
style={styles.image}
source={this.props.source}
resizeMode='contain'
/>
<CameraRoll props={...} onImagePicked={this.onImagePicked}/>
</View>
);
}
To iterate and add views dynamically from array, I'm using following code.
export default class CreateFeedPost extends Component {
constructor(props) {
super(props);
this.state = {
selectedImages: ["1", "2", "3"]
};
}
render() {
let animation = {};
let color = Platform.OS === "android"
? styleUtils.androidSpinnerColor
: "gray";
return (
<View style={{ flex: 1, flexDirection: "column" }}>
<View style={styles.topView}>
<View style={styles.closeButtonView}>
<TouchableHighlight
underlayColor="transparent"
style={styles.closeButton}
onPress={this._closeButtonClicked.bind(this)}
>
<Icon name="times" color="#4A4A4A" size={20} />
</TouchableHighlight>
</View>
<View style={styles.postButtonView}>
<TouchableHighlight
underlayColor="transparent"
style={styles.postButton}
onPress={this._postButtonClicked.bind(this)}
>
<Text style={styles.postButtonText}>Post</Text>
</TouchableHighlight>
</View>
</View>
<View style={styles.profileContainer}>
<View style={{ width: 65, height: 65, padding: 10 }}>
<Image
source={{ uri: global.currentUser.USER_PROFILE_PIC }}
style={styles.profileImage}
/>
</View>
<View style={[styles.middleTextView]}>
<Text style={[styles.memberName]}>
{global.currentUser.USER_NAME}
</Text>
</View>
</View>
<Animated.ScrollView
style={{ flex: 1 }}
scrollEventThrottle={1}
showsVerticalScrollIndicator={false}
{...animation}
>
<View>
<TextInput
ref="postTextInputRef"
placeholder="So, What's up?"
multiline={true}
autoFocus={true}
returnKeyType="done"
blurOnSubmit={true}
style={styles.textInput}
onChangeText={text => this.setState({ text })}
value={this.state.text}
onSubmitEditing={event => {
if (event.nativeEvent.text) {
this._sendCommentToServer(event.nativeEvent.text);
this.refs.CommentTextInputRef.setNativeProps({ text: "" });
}
}}
/>
</View>
</Animated.ScrollView>
<KeyboardAvoidingView behavior="padding">
<ScrollView
ref={scrollView => {
this.scrollView = scrollView;
}}
style={styles.imagesScrollView}
horizontal={true}
directionalLockEnabled={false}
showsHorizontalScrollIndicator={false}
decelerationRate={0}
snapToInterval={100}
snapToAlignment={"start"}
contentInset={{
top: 0,
left: 0,
bottom: 0,
right: 0
}}
>
{this.state.selectedImages.map(function(name, index) {
return (
<View style={styles.imageTile} key={index}>
<View style={styles.imageView}>
<TouchableHighlight
underlayColor="transparent"
style={styles.imageRemoveButton}
onPress={() => this._imageRemoveButtonClicked.bind(this)}
>
<Icon name="times" color="#4A4A4A" size={20} />
</TouchableHighlight>
</View>
</View>
);
})}
</ScrollView>
<TouchableHighlight
underlayColor="transparent"
style={styles.cameraButton}
onPress={this._cameraButtonClicked.bind(this)}
>
<View style={styles.cameraButtonView}>
<Icon name="camera" color="#4A4A4A" size={20} />
<Text style={styles.cameraButtonText}>Add Pic</Text>
</View>
</TouchableHighlight>
</KeyboardAvoidingView>
</View>
);
}
_closeButtonClicked() {
this.props.navigator.pop();
}
_postButtonClicked() {}
_cameraButtonClicked() {
this.props.navigator.push({
title: "All Photos",
id: "photoBrowser",
params: {
limit: 3,
selectedImages: this.state.selectedImages
}
});
}
_imageRemoveButtonClicked() {
console.log("yes do it");
}
}
I'm loading code in the render method. If I write the function imageRemoveButtonClicked outside render method, it's giving an error saying that 'Cannot read property bind of undefined'. Don't know what to do. Can some one please help me in this.
Use arrow functions and class property feature. For more information about binding patterns read this article. Try to add your method as:
export class App extends Component {
yourMapFunction = () => {
yourCode...
}
}
I believe the problem is that you are not using an arrow function as the argument to this.state.selectedImages.map(). If you want to access this inside an inner function, you should use the arrow function syntax. The standard syntax does not capture this.
this.state.selectedImages.map((name, index) => {
return (...);
})