I'm using react native List to generate a Dynamic list using a custom data array and in the ListItem. I am using nested checkbox in every list item and I can't set the switch. I tried it by setting array of state but it doesn't seem to work.
This is my list
const DashboardList = props => (
<List
style={props.ListStyle}
dataArray={props.ListData}
renderRow={item => <ListContent ListData={item} />}
/>
);
This is my listItem
class ListContent extends Component {
static navigationOptions = {
title: "ListContent",
header: null
};
constructor(props) {
super(props);
this.state = {
IsContentVisible: false
};
}
render() {
return (<ListItem>
<View>
<View>
<Left>
<View>
<CheckBox
checked={this.state.checked}
onPress={checked => {
this.setState({ checked: !this.state.checked });
this.props.CheckBoxChange(this.props.ListItem,
checked);}}/>
</View>
<View>
<Image
source={this.props.ListItem.DataFolderContent[0].Image}
resizeMethod="auto"
resizeMode="contain"
/>
</View>
<View>
<Text>
{this.props.ListItem.Name}
</Text>
</View>
</Left>
<Body>
<View>
<Badge style={{ backgroundColor: "#eeae30" }}>
<Text>{this.props.ListItem.DataFolderContent.length}
</Text>
</Badge>
</View>
</Body>
<Right >
<Switch
onTintColor="#eeae30"
value={this.state.switched}
onValueChange={() => {
this.setState({ switched: !this.state.switched });
this.props.SwitchToggled(this.props.ListItem);
}}
/>
</Right>
</View>
</View>
</ListItem>;
}
}
Related
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
Dynamically generate a TextInput when you press a button but I can’t get the value that the user digits,try to use states but I can’t because it’s not with the other general textInputs but it’s imported as Field.
try to create a state in the component file and move it to the general view and print it to see if it works and not...is there any way to bring this state?
general view:
import Campo from './campoInput';
constructor(props){
super(props);
this.state={
Cbusto:"",
Ccintura:"",
Ccadera:"",
valueArray: []
};
this.addNewEle = false;
}
agregarCampo=()=>{
this.addNewEle = true;
const newlyAddedValue = { text: 'prueba'};
this.setState({
valueArray: [...this.state.valueArray, newlyAddedValue]
});
}
render(){
return(
------Here are the normal textInput-----
<View style={{ flex: 1, padding: 4 }}>
{this.state.valueArray.map((ele) => {
return <Campo item={ele} />;
})}
</View>
<View style={styles.flex}>
<View style={styles.ButtonAdd}>
<Button
title="Add input"
color="#B13682"
onPress={this.agregarCampo}
></Button>
</View>
)
}
Component:
constructor(props){
super(props);
this.state={
info:""
};
}
render(){
return(
<View>
<Text>pruba:{this.props.item.text}</Text>
<View style={styles.input}>
<TextInput onChangeText={(text) => this.setState({info:text})}></TextInput>
</View>
</View>
)
}
can be solved by adding the onChangeText event to the Field component in the overview and in the same way in the TextInput of the component being imported, using props status
General view:
<View style={{ flex: 1, padding: 4 }}>
{this.state.valueArray.map((ele) => {
return <Campo item={ele}
onChangeText={(text) => this.setState({ info: text })}
/>;
})}
</View>
Component
<View>
<Text>pruba:{this.props.item.text}</Text>
<View style={styles.input}>
<TextInput onChangeText={this.props.onChangeText}></TextInput>
</View>
</View>
When I want to distribute my data from my firebase database to my React Native mobile application in the form of a carousel with SwiperFlatList, an error is displayed
each time my class is updated, showing me as follows: "Invariant violation: scrolltoindex out of range: requested Nan but maximum is 1."
By hiding the error the carousel works well but it can be very annoying and cause problems during its build.
Here are my codes:
My Render function :
renderPost=post=>{
return(
<View style={styles.card}>
<HorizontalCard title={post.title_new} desc={post.text} img={post.image} />
</View>
)
}
HorizontalCard Component :
export default class HorizontalCard extends Component {
constructor(props) {
super(props);
}
static propTypes = {
screen: PropTypes.string,
title: PropTypes.string,
desc: PropTypes.string,
img: PropTypes.string,
};
state = {
modalVisible: false,
};
setModalVisible(visible) {
this.setState({modalVisible: visible});
}
render() {
return (
<View style={{flex: 1}}>
<TouchableOpacity
onPress={() => {
this.setModalVisible(true);
}}
style={styles.container}>
<Modal
animationType="slide"
transparent={false}
visible={this.state.modalVisible}
onRequestClose={() => {
alert('Modal has been closed.');
}}>
<View style={{marginTop: 22}}>
<View>
<Text>{this.props.desc}</Text>
<Button
title="Fermer la fenêtre"
onPress={() => {
this.setModalVisible(!this.state.modalVisible);
}} />
</View>
</View>
</Modal>
<View style={styles.card_discord}>
<Image style={styles.card_discord_img} source={{ uri: this.props.img }} />
<LinearGradient
start={[1.0, 0.5]}
end={[0.0, 0.5]}
colors={['rgba(51, 51, 51, 0.2)', '#333333', '#333333']}
style={styles.FadeAway}>
<View style={styles.FadeAway}>
<Text h4 style={styles.FadeAway_h2}>
{this.props.title}
</Text>
<Text style={styles.FadeAway_p}>{this.props.desc}</Text>
</View>
</LinearGradient>
</View>
</TouchableOpacity>
</View>
);
}
}
My HomeScreen component (page where the error appears) :
export default class HomeScreen extends Component {
constructor(props) {
super(props);
this.ref = Fire.shared.firestore.collection('posts')
this.useref=
this.state={
dataSource : [],
}
}
feedPosts = (postSnapShot) =>{
const post = [];
postSnapShot.forEach((doc) => {
const {uid,text,timestamp,title_new,image} = doc.data();
const data=Fire.shared.firestore
.collection('users')
.doc(uid)
.get()
.then(doc=>{
post.push({
text,
timestamp,
title_new,
image,
uid,
})
this.setState({
dataSource : post,
});
})
});
}
renderPost=post=>{
return(
<View style={styles.card}>
<HorizontalCard title={post.title_new} desc={post.text} img={post.image} />
</View>
)
}
render() {
return (
<ThemeProvider>
<ScrollView>
<SwiperFlatList
autoplay
autoplayDelay={5}
index={0}
autoplayLoop
autoplayInvertDirection
data={this.state.dataSource}
renderItem={({item})=>this.renderPost(item)}
/>
...
<ScrollView>
</ThemeProvider>
)
}
}
I am trying to make cards clickable and expandable, but when updating state, it occurs in all the cards . I just want to make the state change in only one card (the one on which the event occurred , in this case, a click event).
However, the state gets updated for every other cards. Currently this does work on one item but it needs to work on others too.
export default class TransactionCards extends React.Component {
constructor(props) {
super(props);
this.state = {
isHidden: true
};
}
toggleHidden() {
this.setState({
isHidden: !this.state.isHidden
});
}
renderCards() {
return this.props.data.map((card, index) => (
<View key={card.id}>
<View style={styles.dateContainer}>
<Text style={styles.dateText}>{card.time}</Text>
</View>
<Card>
<TouchableWithoutFeedback onPress={this.toggleHidden.bind(this)}>
<View>
<View>
<View style={styles.transactionContainer}>
<Image
style={styles.countryImage}
source={require("../../assets/images/us-country-icon.png")}
/>
<Text style={styles.transactionTitle}>{card.title}</Text>
<Text style={styles.transactionAmount}>{card.cost}</Text>
<Icon
name={card.transactionType == "debit" ? "log-out" : "log-in"}
type="feather"
color={card.transactionType == "debit" ? "red" : "green"}
size={18}
iconStyle={styles.transactionIcon}
/>
</View>
<View>
<Text style={styles.transactionBankDetails}>{card.bankDetails}</Text>
</View>
</View>
{/* Setting visiblity condition */}
{!this.state.isHidden && (
<View>
<Divider style={styles.CardItemDivider} />
<View display="flex" flexDirection="row" justifyContent="space-between">
<View>
<Text style={styles.transactionBankDetails}>
{card.transactionCategory}
</Text>
<Text style={styles.transactionBankDetails}>{card.accountName}</Text>
</View>
<View>
<Icon name="bell" type="feather" iconStyle={styles.transactionIcon} />
</View>
</View>
</View>
)}
</View>
</TouchableWithoutFeedback>
</Card>
</View>
));
}
render() {
return <ScrollView showsVerticalScrollIndicator={false}>{this.renderCards()}</ScrollView>;
}
}
Create a new component for card and pass required params
Do the toggleHidden function inside the Card component
then inside the file it should look like
<Card {SetYourPassedParams} />
......
You have two items connected to the same state value on the same state. If you want to separate the behaviours you have to separate the values too.
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>);
}