I am fetching data from http://retailsolution.pk/api/allhome I want to display the title of the product and then all the child products below it, I am getting this output: Here's my code:
class App extends Component {
constructor(props) {
super(props);
this.state = {
Deals: []
};
}
componentWillMount() {
axios
.get("https://retailsolution.pk/api/allhome")
.then(response => this.setState({ Deals: response.data.Deals }));
}
_renderItem(item) {
return (
<View style={{ width: 100, height: 130 }}>
<Image
style={{ width: 100, height: 100 }}
source={{ uri: item.image }}
/>
<Text numberOfLines={1} style={{ flex: 1 }}>
{" "}
{item.name}
</Text>
</View>
);
}
renderTitle() {
return this.state.Deals.map(deal => (
<Text key={deal.parent.id} style={styles.text}>
{deal.parent.name}
</Text>
));
}
renderImage() {
return this.state.Deals.map(deal => (
<FlatList
key={deal.child.product_id}
style={{ marginTop: 5 }}
horizontal
ItemSeparatorComponent={() => <View style={{ width: 5 }} />}
renderItem={({ item }) => this._renderItem(item)}
data={[deal.child]}
/>
));
}
render() {
console.log(this.state.Deals);
return (
<View style={{ flex: 1, marginLeft: 8, marginRight: 8, marginTop: 10 }}>
{this.renderTitle()}
{this.renderImage()}
</View>
);
}
}
In my case {this.renderTitle()} gets execute first and maps every value from the api to the app and then {this.renderImage()} maps all flatlists to the app.
Is there any way I can run this.renderImage() after every iteration of rhis.renderTitle()?
You will have to do it using nested loop.
Try something like this -
{this.state.Deals.map(deal => {
return (
<div>
<Text key={deal.parent.id} style={styles.text}>
{deal.parent.name}
</Text>
{deal.child.map(item => {
return (
<FlatList
key={item.product_id}
style={{ marginTop: 5 }}
horizontal
ItemSeparatorComponent={() => <View style={{ width: 5 }} />}
renderItem={({ item }) => this._renderItem(item)}
data={[item]}
/>
);
})}}
</div>
);
})}
A way to do it is to call a function after the fetch that will create the section data for the Flatlist with the correct format:
sections = [{title: 'Latest', data:["The products data array"]}, {title: 'second section', data : ["Other products"]}, and so on...]`
Related
I have list of product and everyone have two inputs I want to use formik to validate and collected all the filled data in those inputs but I'm facing a problem with onChange of hook useField() after submitting the data an error pops up
the error:
ERROR Warning: This synthetic event is reused for performance
reasons. If you're seeing this, you're accessing the property
defaultPrevented on a released/nullified synthetic event. This is
set to null. If you must keep the original synthetic event around, use
event.persist(). See https://reactjs.org/link/event-pooling for more
information.
screenshot:
the code:
<Formik
initialValues={{
product: ProductListDB.map((pro: any) => {
return (
{
packingQuantity: '',
unitQuantity: ''
}
)
})
}}
onSubmit={(values) => {
console.log("values: ", values)
}}
>
{({
handleChange,
handleBlur,
handleSubmit,
touched,
values,
errors,
isValid,
dirty,
}) => (
<>
<FieldArray name="product">
{({ insert, remove, push }) => (
<>
<FlatList
data={values.product}
renderItem={({ item, index }) => (
<ProductItemComponent key={index} values={item} product={ProductListDB[index]} index={index}/>
)}
keyExtractor={(item, index) => index.toString()}
// refreshControl={
// <RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
// }
/>
</>
)}
</FieldArray>
<Button
style={styles.buttonStyle}
disabled={!isValid || !dirty || loading}
onPress={handleSubmit}
accessoryLeft={loading ?
<View style={{ justifyContent: 'center', alignItems: 'center' }}><Spinner size='small' status='info' /></View>
: IconComponent((I18nManager.isRTL ? evaIcons.frontArrow : evaIcons.backArrow), Colors.white500)
}
>
{(props) => (
<Text
{...props}
style={{
fontFamily: I18nManager.isRTL ? 'almarai-bold' : 'montserrat-bold',
color: Colors.white500,
fontSize: 17,
marginRight: 35,
}}
>
{t('unloadScreen:submit')}
</Text>
)}
</Button>
</>
)}
</Formik>
ProductItemComponent:
const ProductItem: FC<Props> = ({ product, values, index }: Props): ReactElement => {
const [packingQuantity, PQMeta, PQhelpers] = useField({ name: `product[${index}].packingQuantity` })
const [unitQuantity, UQMeta, UQhelpers] = useField({ name: `product[${index}].unitQuantity` })
const { setFieldValue } = useFormikContext();
const validateValue = (v: string): number => {
if (isNaN(parseInt(v))) {
return (0);
} else {
return (parseInt(v))
}
};
return (
<View
style={styles.container}
>
<View style={{ flex: 2, flexDirection: 'row', justifyContent: 'space-around', alignItems: 'center' }}>
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<CachedImage
source={{
uri: `${env.apiUrl.concat(env.productImagePngById.replace(":productId", product.id)).concat("?thumbnail=true")}`, // (required) -- URI of the image to be cached
expiresIn: 43200, // 12 hours in seconds (optional), if not set -- will never expire and will be managed by the OS
}}
cacheKey={`${product.id}-thumbnail`} // (required) -- key to store image locally
placeholderContent={( // (optional) -- shows while the image is loading
<Image
source={{
uri: productDefaultImage
}}
resizeMode="contain"
style={{ width: 100, height: 70 }}
/>
)}
resizeMode="contain" // pass-through to <Image /> tag
style={{ width: 100, height: 70 }}
/>
</View>
<View style={{ flex: 3, justifyContent: 'center' }}>
<Text style={styles.nameStyleText}>{product.name}</Text>
<Text style={styles.nameStyleText}>{product.reference}</Text>
</View>
</View>
<View style={{ flexDirection: 'row' }}>
<Input
style={{ alignItems: 'center', marginRight: 10 }}
value={packingQuantity.value}
onChange={(value) => {
setFieldValue(packingQuantity.name, value)
}}
// onChangeText={handleChange("amount")}
keyboardType="numeric"
/>
<Input
style={{ alignItems: 'center' }}
value={unitQuantity.value}
onChange={(value) => {
setFieldValue(unitQuantity.name, value)
}}
// onChangeText={(value) => setUnitQuantity(validateValue(value))}
keyboardType="numeric"
/>
</View>
</View>
);
}
What I want is to increment the count for each individual item but when I'm trying to do that it changes for every element rendered. I'm getting data from API and fetching it to redux store and mapping over the array. But could not differ each element.
return (
<View style={styles.Container}>
{movies ? (
movies.map((data,i) => {
const IMAGE_URL = data.image;
return (
<Card key={i}>
<View style={styles.Content}>
<Card.Cover style={styles.image} source={{ uri: IMAGE_URL }} />
<ScrollView style={styles.ProductDetails}>
<Text
style={{
fontSize: 15,
fontWeight: "bold",
paddingBottom: 10,
}}
>
{data.title}
</Text>
<Paragraph>{data.description}</Paragraph>
</ScrollView>
</View>
<View style={styles.BottomButtonView}>
<View style={{ flexDirection: "row", alignItems: "center" }}>
<TouchableOpacity
key={i}
style={styles.ButtonContainer}
onPress={() => setCount(count+1)}
>
<Text style={{ color: "white" }}>+</Text>
</TouchableOpacity>
<Text style={{ padding: 10 }}>{count}</Text>
<TouchableOpacity
style={styles.ButtonContainer}
onPress={() => console.log('dec')}
>
<Text style={{ color: "white" }}>-</Text>
</TouchableOpacity>
</View>
<TouchableOpacity
onPress={() => console.log("Added to Cart")}
style={styles.AddToCart}
>
<Text style={{ color: "white" }}>${data.price}</Text>
</TouchableOpacity>
</View>
</Card>
);
})
) : (
<Text>Getting Data....</Text>
)}
</View>
);
}
Updating an Item in an Array :
function updateObjectInArray(array, action) {
return array.map((item, index) => {
if (index !== action.index) {
// This isn't the item we care about - keep it as-is
return item
}
// Otherwise, this is the one we want - return an updated value
return {
...item,
...action.item
}
})
}
for Example :
const changePrice = (itemArray, item) => {
return itemArray.map(reduxItem => {
if(reduxItem.id === item.id){
reduxItem.price = reduxItem.price + reduxItem.price
}
return reduxItem
});
};
Currently, I am using this logic to render data on the basis of results from a grapqhl query. This works fine:
const contacts = () => {
const { loading, error, data } = useUsersQuery({
variables: {
where: { id: 1 },
},
});
if (data) {
console.log('DATA COMING', data);
const contactName = data.users.nodes[0].userRelations[0].relatedUser.firstName
.concat(' ')
.concat(data.users.nodes[0].userRelations[0].relatedUser.lastName);
return (
<View style={styles.users}>
<View style={styles.item} key={data.users.nodes[0].id}>
<Thumbnail
style={styles.thumbnail}
source={{
uri:
'https://cdn4.iconfinder.com/data/icons/avatars-xmas-giveaway/128/girl_avatar_child_kid-512.png',
}}></Thumbnail>
<Text style={styles.userName}>{contactName}</Text>
</View>
</View>
);
}
};
return (
<SafeAreaView style={{ flex: 1 }}>
<Container style={{ flex: 1, alignItems: 'center' }}>
<Item style={styles.addToWhitelist}>
<Icon name="add" onPress={() => navigation.navigate('AddContact')} />
<Text style={styles.addToContactTitle}>Add contact</Text>
</Item>
<Text onPress={() => navigation.navigate('Home')}>Zurück</Text>
<View style={{ width: moderateScale(350) }}>
<Text>Keine Kontacte</Text>
</View>
{contacts()}
{/* <ContactList data={userData}></ContactList> */}
</Container>
</SafeAreaView>
);
};
However, when I make a separate component :
export const ContactList: React.FunctionComponent<UserProps> = ({
data,
}) => {
console.log('user called');
if (!data) return null;
console.log('DATA COMING', data);
const contactName = data.users.nodes[0].userRelations[0].relatedUser.firstName
.concat(' ')
.concat(data.users.nodes[0].userRelations[0].relatedUser.lastName);
return (
<View style={styles.users}>
<View style={styles.item} key={data.users.nodes[0].id}>
<Thumbnail
style={styles.thumbnail}
source={{
uri:
'https://cdn4.iconfinder.com/data/icons/avatars-xmas-giveaway/128/girl_avatar_child_kid-512.png',
}}></Thumbnail>
<Text style={styles.userName}>{contactName}</Text>
</View>
</View>
);
};
and call it like this:
const [userData, setUserData] = useState<UsersQueryHookResult>('');
const contacts = () => {
console.log('running');
const { loading, error, data } = useUsersQuery({
variables: {
where: { id: 1 },
},
});
if (data) {
setUserData(data);
}
};
return (
<SafeAreaView style={{ flex: 1 }}>
<Container style={{ flex: 1, alignItems: 'center' }}>
<Item style={styles.addToWhitelist}>
<Icon name="add" onPress={() => navigation.navigate('AddContact')} />
<Text style={styles.addToContactTitle}>Add contact</Text>
</Item>
<Text onPress={() => navigation.navigate('Home')}>Zurück</Text>
<View style={{ width: moderateScale(350) }}>
<Text>Keine Kontacte</Text>
</View>
{/* {contacts()} */}
<ContactList data={userData}></ContactList>
</Container>
</SafeAreaView>
);
};
However, this gives me a too many re-renders issue. What am I missing? Probably something basic. I also tried using useEffects but I can't run a graphql query hook inside it. That gives me an invalid hook call error.
It seems your running in an endless recursion.
If you call contacts in you render block it causes a setState (through setUserData) which renders, so contacts is called once again and so on till infinite (or till the error).
I have a component called MDropDowlList which render the following view :
MDropDownList.js
render() {
return (
<View>
<View
style={{
backgroundColor: '#fff',
position: 'absolute',
left: 0,
right: 0,
top: 0,
maxHeight: 200,
}}
>
{
this.props.searchable && (
<TextInput
style={{ zIndex: 198 }}
onChangeText={(text) => this.search(text)}
/>
)
}
<FlatList
data={this.state.data}
keyboardShouldPersistTaps="always"
nestedScrollEnabled={true}
contentContainerStyle={{ zIndex: 199 }}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item, index }) => (
<TouchableHighlight
key={index.toString()}
style={{
padding: 10,
backgroundColor: '#fff',
}}
underlayColor="#5897fb"
onPress={() => this.props.handleOnPress(item)}
>
<MText size={18}>{item}</MText>
</TouchableHighlight>
)}
/>
</View>
</View>
);
}
Then I have called this component to another component called MDropDown which render method is as below :
MDropDown.js
render() {
return (
<View style={{ marginVertical: 5, zIndex: 3}}>
...
{/* Another components */}
...
{
this.state.displayDropDown && (
<MDropDownList data={this.props.data} searchable={this.props.searchable} handleOnPress={(item) => this.handleOnPress(item)} />
)
}
</View>
);
}
Now finally I called my MDropDown component in my main screen as follow :
render() {
return (
<KeyboardAvoidingView style={{ flex: 1, backgroundColor: Colors.bgGray }} behavior="padding">
<ScrollView showsVerticalScrollIndicator={false} contentContainerStyle={styles.container} keyboardShouldPersistTaps="always" nestedScrollEnabled={true}>
<View style={{ backgroundColor: '#fff', padding: 10 }}>
<MDropDown
label="Category"
placeholder="Select Category"
data={["test1", "test2", "test3", "test4", "test5"]}
onSelectItem={(selectedItem) => alert(selectedItem)}
searchable={true}
/>
</View>
</ScrollView>
</KeyboardAvoidingView>
)
}
But I am not able to access Flatlist item or TextInput of MDropDownList component. Even I am not able to focus TextInput available in MDropDownList.
Please help me what's going wrong here ???
Note : This issue is only on android, on ios it is working properly. On ios I am able to click flatlist item and focus TextInput as well as.
Tried using zIndex to the absolute view?
Add property of zIndex : 500 to the style.
How do I map over two different arrays in react native? In my case I'm fetching a response from server and mapping over it. Also there is another array named images which I want to list along with the fetched response from server.But the second mapping is looping within the first one. How do I separate it from the first?Following is my code.
sample code
<ScrollView>
{this.state.workers.map(a =>
<CardSection>
<TouchableOpacity onPress={() => this.popupDialog.show()}>
<View style={{ marginTop: 10, marginLeft:120}}>
{images.map(b =>
<Image
style={{ height: 100, width: 100 }}
source={{ uri: b.image }}
/>
)}
<Text style={{marginLeft:20, fontSize:20}}>{a.work_type}</Text>
</View>
</TouchableOpacity>
</CardSection>
)}
workers array is the json response I'm fetching from server.images array is as folows
export const images = [
{
image:'http://localhost:3000/Images/carpenter.png',
text:'hello'
},
{
image:'http://localhost:3000/Images/electrician.png',
text:'hii'
},
]
Also this how workers array looks like
updated
[
{
"sl.no": 1,
"worker_id": "wr_1",
"work_type": "carpenter",
"phone_num": "3456789243"
},
{
"sl.no": 2,
"worker_id": "wr_2",
"work_type": "electrician",
"phone_num": "345221344"
},
{
"sl.no": 3,
"worker_id": "wr_3",
"work_type": "plumber",
"phone_num": "8976545677"
}
]
You can simply move it above the first map and save the result:
render() {
const imagesToRender = images.map(b => {
return (
<Image
style={{ height: 100, width: 100 }}
source={{ uri: b.image }}
/>
);
});
return (
<ScrollView>
{this.state.workers.map(a =>
<CardSection>
<TouchableOpacity onPress={() => this.popupDialog.show()}>
<View style={{ marginTop: 10, marginLeft:120}}>
{imagesToRender}
<Text style={{marginLeft:20, fontSize:20}}>{a.work_type}</Text>
</View>
</TouchableOpacity>
</CardSection>
)}
</ScrollView>
);
}
Also, don't forget to add key props to each Image and each CardSection.
you can easily use flatlist with better performance
import React, { Component } from "react";
import { View, FlatList, TouchableOpacity, Image, Text } from "react-native";
const workers = [
{ id: 1, name: 'Nima', images: [{ image: 'https://www.lens-rumors.com/wp-content/uploads/2014/10/Nikon-AF-S-DX-Nikkor-18-140mm-f3.5-5.6G-ED-VR-sample-images1.jpg', text: 'hello' },{ image: 'https://www.lens-rumors.com/wp-content/uploads/2014/10/Nikon-AF-S-DX-Nikkor-18-140mm-f3.5-5.6G-ED-VR-sample-images1.jpg', text: 'hello' },{ image: 'https://www.lens-rumors.com/wp-content/uploads/2014/10/Nikon-AF-S-DX-Nikkor-18-140mm-f3.5-5.6G-ED-VR-sample-images1.jpg', text: 'hello' },{ image: 'https://www.lens-rumors.com/wp-content/uploads/2014/10/Nikon-AF-S-DX-Nikkor-18-140mm-f3.5-5.6G-ED-VR-sample-images1.jpg', text: 'hello' }] },
{ id: 2, name: 'Mike', images: [{ image: 'https://www.dike.lib.ia.us/images/sample-1.jpg/image', text: 'goodby' },{ image: 'https://www.dike.lib.ia.us/images/sample-1.jpg/image', text: 'goodby' },{ image: 'https://www.dike.lib.ia.us/images/sample-1.jpg/image', text: 'goodby' },{ image: 'https://www.dike.lib.ia.us/images/sample-1.jpg/image', text: 'goodby' },] },
]
class Test extends Component {
constructor(props) {
super(props);
this.state = {
workers: workers
};
}
_renderItem = ({ item }) => {
console.log(item);
return (
<View style={{ flex: 1 }}>
<TouchableOpacity>
<View style={{ marginTop: 10, marginLeft: 120 }}>
{item.images.map((b, index) => {
console.log(b.image);
return (
<View key={index}>
<Image
style={{ height: 100, width: 100 }}
source={{ uri: b.image }}
/>
<Text
style={{ marginLeft: 20, fontSize: 20, color: "black" }}
>
{b.text}
</Text>
</View>
);
})}
<Text style={{ marginLeft: 20, fontSize: 20, color: "black" }}>
{item.name}
</Text>
</View>
</TouchableOpacity>
</View>
);
};
_keyExtractor = (item, index) => item.id.toString();
render() {
return (
<FlatList
data={this.state.workers}
extraData={this.state}
keyExtractor={this._keyExtractor}
renderItem={this._renderItem}
/>
);
}
}
export default Test;
If the images don't load, it's probably because you pass the wrong properties to the <Image /> component. Look up the docs for <Image /> component or replace it with <img /> instead and pass the url string of the image to the src attribute.
getImageUri(worker) {
// Decide which image to return for a certain type of worker
// For more workers and images, change the following logic
if(worker.work_type == 'carpenter') {
return images[0].image;
} else if(worker.work_type == 'electrician') {
return images[1].image;
} else {
return '';
}
}
render() {
...
<ScrollView>
{this.state.workers.map((a, index) =>
<CardSection>
<TouchableOpacity onPress={() => this.popupDialog.show()}>
<View style={{ marginTop: 10, marginLeft:120}}>
<Image
style={{ height: 100, width: 100 }}
source={{ uri: this.getImageUri(a)}}
/>
<Text style={{marginLeft:20, fontSize:20}}>{a.work_type}</Text>
</View>
</TouchableOpacity>
</CardSection>
)}
</ScrollView>
...
}