I'm creating a shopping cart in react native with redux but I'm getting an error
undefined is not an object (evaluating 'this.props.items.length')
I'm following a tutorial of react redux someone suggest me to follow that tutorial to make cart,
but I'm getting that error
can someone please tell me what's going on.., below is my code
class Cart extends Component {
render() {
let addedItems = this.props.items.length ? (
<FlatList
data={this.props.items}
key={(items) => items.id.toString()}
numColumns={2}
renderItem={({ item }) => (
<View>
<Image style={styles.image} source={item.image} />
<View style={styles.detailContainer}>
<Text style={styles.title}>{item.title}</Text>
<Text style={styles.subTitle} numberOfLines={1}>
Quantity: {item.quantity}
</Text>
<Text style={styles.price}>Rs {item.price}</Text>
</View>
<TouchableOpacity>
<View style={styles.buy}>
<Text>Remove</Text>
</View>
</TouchableOpacity>
</View>
)}
/>
) : (
<Text>Nothing</Text>
);
return (
<View>
<View>
<Text>You Have Ordered:</Text>
</View>
<View>{addedItems}</View>
</View>
);
}
}
const mapStateToProps = (state) => {
return {
items: state.addedItems,
};
};
try like this
let addedItems = this.props.items && this.props.items.length ? (
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 made 2 screens one home screen and 2nd edit screen I need to edit data of home screen from edit screen and save it and that data should also update in home and detail screen. How can I do this without redux or context. Can anyone tell me.
Home.js
class Home extends Component {
state = {
post: [
{
key: "1",
title: "A Good Boi",
des: "He's a good boi and every one know it.",
image: require("../assets/dog.jpg"),
},
],
};
render() {
return (
<FlatList
data={this.state.post}
renderItem={({ item }) => (
<>
<TouchableOpacity
activeOpacity={0.7}
onPress={() => this.props.navigation.navigate("Edit", item)}
style={styles.Edit}
>
<MaterialCommunityIcons
name="playlist-edit"
color="green"
size={35}
/>
</TouchableOpacity>
<Card
title={item.title}
subTitle={item.des}
image={item.image}
onPress={() => this.props.navigation.navigate("Details", item)}
/>
</>
)}
/>
Edit.js
class ListDetails extends Component {
render() {
const listing = this.props.route.params;
return (
<View>
<Image style={styles.image} source={listing.image} />
<View style={styles.detailContainer}>
<AppTextInput value={listing.title} />
<AppTextInput value={listing.des} />
</View>
<AppButton
text="Save"
onPress={() => this.props.navigation.goBack("Home")}
/>
</View>
Details.js
const listing = this.props.route.params;
return (
<View>
<Image style={styles.image} source={listing.image} />
<View style={styles.detailContainer}>
<Text style={styles.title}>{listing.title}</Text>
<Text style={styles.des}>{listing.des}</Text>
</View>
</View>
);
You can pass a function from your home screen to setState for it in your edit screen. In case navigating to edit screen causes the home screen unmounted, you can change the navigate method to push of stack navigator (I haven't tested yet). The code now should look like:
HomeScreen.js:
onEdit=(data)=>{
setState(...);
}
...
<TouchableOpacity
activeOpacity={0.7}
onPress={() => this.props.navigation.navigate("Edit", {item, onEdit})} //use push instead if error occured
style={styles.Edit}
>
...
Edit.js
class ListDetails extends Component {
render() {
const {item:listing, onEdit} = this.props.route.params;
return (
<View>
<Image style={styles.image} source={listing.image} />
<View style={styles.detailContainer}>
<AppTextInput value={listing.title} />
<AppTextInput value={listing.des} />
</View>
<AppButton
text="Save"
onPress={() => { onEdit(...); this.props.navigation.goBack("Home");}}
/>
</View>
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 don't know the reason of the error:
{ (questionList.length > 0) && questionList.map((item,index)
=>
<View key={index} style={styles.oneMonthV}>
<Text
style={[styles.textTGray, styles.fontTSize, TEXT_STYLE, styles.fontTQuestion]}>{item.content}</Text>
<View style={styles.VTags}>
{item.optionsList.map((item1, index1) =>
<TouchableOpacity key={index1}
onPress={this._onPressButton.bind(this, index1)}>
<View>
{ (item1.selected) ? (<TagCell modeColor='red'
content={item1.content}></TagCell>) : (
<TagCell
content={item1.content}></TagCell>) }
</View>
</TouchableOpacity>
) }
</View>
</View>
)}
The error show at
<View key={index} style={styles.oneMonthV}> . Why index is undefined.
I type the code below in the chrome console is correct.
['xxx','xx','xxx','www'].map((item,index) => { console.log(`index==${index}`); console.log(`item==${item}`);
return (
item + 'hahaha' )});
result:
> index==0
item==xxx
index==1
item==xx
index==2
item==xxx
index==3
item==www
(4) ["xxxhahaha", "xxhahaha", "xxxhahaha", "wwwhahaha"]
I think my code is correct.
Who knows the reason of this error?
As this error is too difficult.
I add more code of this question.
render() {
const {questionList} = this.props;
return (
<ScrollView style={styles.container}>
{ (questionList.length > 0) && questionList.map((item,index) => (
<View key={index} style={styles.oneMonthV}>
<Text
style={[styles.textTGray, styles.fontTSize, TEXT_STYLE, styles.fontTQuestion]}>{item.content}</Text>
<View style={styles.VTags}>
{item.optionsList.map((item1, index1) => (
<TouchableOpacity key={index1}
onPress={this._onPressButton.bind(this, index1)}>
<View>
{ (item1.selected) ? (<TagCell modeColor='red'
content={item1.content}></TagCell>) : (
<TagCell
content={item1.content}></TagCell>) }
</View>
</TouchableOpacity>
)
) }
</View>
</View>
)
)}
</ScrollView>
);
}
There are few things going wrong, whenever you make use of arrow function, you should start with the body of the function on the same line. Also its a good practise to wrap it in (), also your check is unnecessary since if no element is present it wont map, however you must check for undefined. Also change {} to {}.
{questionList && questionList.map((item,index) => (
<View key={index} style={styles.oneMonthV}>
<Text
style={[styles.textTGray, styles.fontTSize, TEXT_STYLE, styles.fontTQuestion]}>{item.content}</Text>
<View style={styles.VTags}>
{item.optionsList.map((item1, index1) => (
<TouchableOpacity key={index1}
onPress={this._onPressButton.bind(this, index1)}>
<View>
{ (item1.selected) ? (<TagCell modeColor='red'
content={item1.content}></TagCell>) : (
<TagCell
content={item1.content}></TagCell>) }
</View>
</TouchableOpacity>
)
) }
</View>
</View>
)
)}
Hey guys can anybody help me way out of this problem. My problem is related to improving UX of the app. I have rendered accordion through a loop which renders data with a button on it. I wanted to change color of the button which is pressed thus helping in UX. But ran into problem as all buttons are turning their colors . The problem is i am rendering color from state can anybody help me out with some different solution.
This is the loop through which i am rendering accordion:
{this.state.services.map((tab, i) => {
return(
<ScrollView key={i} tabLabel={tab.category} style={{flex: 1}}>
{tab.names.map((name, j) => this.renderRow(name))}
</ScrollView>
)
})}
This is the render function:
renderRow (rowData) {
var header = (
rowData.subGroup.length > 1 ? <View>
<Text>{rowData.name}</Text>
</View>
:
<View>
<View style={{flex: 1}}>
<Text>{rowData.name}</Text>
<Text>{rowData.subGroup[0].duration}Min</Text>
</View>
<Text>₹{rowData.subGroup[0].price.M ? rowData.subGroup[0].price.M : rowData.subGroup[0].price.F}</Text>
<TouchableOpacity onPress={() => this._add(rowData.subGroup[0])}>
<View style={styles.buttonView}>
<Text style={styles.buttonText}>ADD</Text>
</View>
</TouchableOpacity>
</View>
);
var content = (
<View style={{flex: 1}}>
{rowData.subGroup.map((sg, i) => {
return(
sg.subGroup ? <View>
<View style={{flex: 1}}>
<Text>{sg.subGroup ? sg.subGroup : sg.name}</Text>
<Text>{sg.duration}Min</Text>
</View>
<Text style={styles.price}>₹{sg.price.M ? sg.price.M : sg.price.F}</Text>
<TouchableOpacity onPress={() => this._add(sg)}>
<View style={styles.buttonView}>
<Text style={styles.buttonText}>ADD</Text>
</View>
</TouchableOpacity>
</View>
:
<View></View>
)
})}
</View>
);
return (
<Accordion
key={rowData.name}
header={header}
content={content}
easing="easeOutCubic"
/>
);
}
can anybody guide me through different approach?
Guys i found out the solution just wrote a simple function and returned true or false. Based on the returned value and ternary operator i change style of selected value
here is a solution:
<View style={this._toggleServiceButtonView(sg) ? styles.removeView : styles.buttonView}>
<Text style={this._toggleServiceButtonView(sg) ? styles.removeText : styles.buttonText}>
{this._toggleServiceButtonView(sg) ? 'REMOVE' : 'ADD'}
</Text>
</View>
Here is the function in which i am just checking if the selected value is in cart array to return true.
_toggleServiceButtonView(data) {
return this.state.cartArr.length != 0 && this.state.cartArr.findIndex((service) => {
if(service.service._id === data._id){
return true;
} else {
return false;
}
}) !== -1
}