I'm working in the react-native android app which connects WordPress woocommerce API and gets data from WordPress and process it. Everything working fine, but I'm a newbie to react-native and can't trigger out how to add a product in the cart. Also, need to show the product in cart page Below I added my code. I hope will get help.
import React, { Component } from "react";
import {
View,
Text,
StyleSheet,
TouchableOpacity,
Platform,
StatusBar,
Image,
TouchableHighlight, ToolbarAndroid,
FlatList,
ScrollView,TextInput,
AsyncStorage
} from "react-native";
import HTMLView from 'react-native-htmlview';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { StackNavigator } from "react-navigation";
import {Icon, Button, Container, Header, Content,Left ,Right,Item,Input,Card,CardItem} from 'native-base'
import { Ionicons,FontAwesome } from '#expo/vector-icons'
import FAIcon from 'react-native-vector-icons/FontAwesome'
import * as CartAction from '../Actions/CartActions';
//Constant declaration
export const GET_PRODUCTS_SUCCESS = 'GET_PRODUCTS_SUCCESS'
export const GET_PRODUCTS_FAILED = 'GET_PRODUCTS_FAILED';
export const GET_CART_SUCCESS = 'GET_CART_SUCCESS';
export const ADD_TO_CART_SUCCESS = 'ADD_TO_CART_SUCCESS';
export const REMOVE_FROM_CART_SUCCESS = 'REMOVE_FROM_CART_SUCCESS';
class Products extends Component {
constructor(props) {
super(props);
this.state = { quantity: 1 };
}
//decrease quantity working
decreaseQuantity = () => {
if(this.state.quantity <= 1) {
return;
} else {
this.setState({
quantity: this.state.quantity - 1
});
}
}
//increase quantity working
increaseQuantitiy = () => {
this.setState({
quantity: this.state.quantity - 1 + 2
});
}
//add to cart NOT WORKING
addToCart(product, quantity) {
return (dispatch) => {
const cartItem = {
"id": product.id,
"image": product.images[0].src,
"name": product.name,
"quantity": quantity
}
dispatch({
type: types.ADD_TO_CART_SUCCESS,
item: cartItem
});
}
}
//Sidemenu Icon
static navigationOptions ={
drawerIcon:(
<FontAwesome name="home" size={30} color="black" />
)
}
//Render Block
render() {
const product = this.props.navigation.state.params.product;
return (
<Container>
//Product View
<ScrollView>
<Image style={styles.image} source={{ uri: product.images[0].src }} />
<View style={{ alignItems: 'center', justifyContent: 'center' }}>
<Text style={{fontSize:18,fontWeight:'bold'}}>{product.name}</Text>
<Text> ₹ {product.price}</Text>
</View>
<View style={{ display: 'flex', flexDirection: 'row', padding: 10, marginLeft: 20, marginBottom: 20 }}>
<View style={{ display: 'flex', flexDirection: 'row', justifyContent: 'center',marginTop:10 }}>
<TouchableOpacity style={styles.decreaseButton} onPress={this.decreaseQuantity}>
<Text> - </Text>
</TouchableOpacity>
<TextInput
style={styles.input}
onChangeText={(quantity) => this.setState({ quantity })}
value={`${this.state.quantity}`}
keyboardType="numeric"
/>
<TouchableOpacity style={styles.inceaseButton} onPress={this.increaseQuantitiy} >
<Text> + </Text>
</TouchableOpacity>
</View>
// ---------------- Adding to cart not working also need to show this product in cart page----------------
<TouchableOpacity style={styles.button} onPress={() => this.addToCart(product, this.state.quantity)} >
<Text style={{ color: '#fff' }}> ADD TO CART </Text>
</TouchableOpacity>
</View>
<HTMLView style={styles.html} value={product.description} />
</ScrollView>
</Container>
)
}
}
export default Products;
Related
Hi everyone i have a problem styling every item present in a flatlist. i find on stackoverflow that you have to use index in the render item but in the render item i have a component that contains all of my items .
What i wanted to reach is this result :
here is my code in the home component:
import React, {useEffect, useState} from 'react';
import {
FlatList,
Pressable,
ScrollView,
StyleSheet,
Text,
View,
} from 'react-native';
import {Audio, Book} from '../types';
import customData from '../books.json';
import BookBox from '../components/BookBox';
import Menu from 'react-native-vector-icons/Entypo';
import Glass from 'react-native-vector-icons/Entypo';
export default function HomeScreen() {
const [books, setBooks] = useState<Book[]>([]);
const [audio, setAudio] = useState<Audio[]>([]);
const [selected, setSelected] = useState(false);
const [selectedAudio, setSelectedAudio] = useState(false);
useEffect(() => {
setBooks(customData);
}, []);
const renderBooks = ({item}: {item: Book}) => <BookBox book={item} />;
return (
<ScrollView nestedScrollEnabled={true} style={styles.scrollContainer}>
<View style={styles.searchContainer}>
<Menu name={'menu'} size={30} color={'black'} />
<Text style={styles.textSearch}>All Books</Text>
<Glass name={'magnifying-glass'} size={30} color={'black'} />
</View>
<View style={styles.AudioOrEbookContainer}>
<Pressable
onPress={() => setSelected(!selected)}
style={{backgroundColor: selected ? 'white' : 'transparent'}}>
<View style={styles.btn}>
<Text>Ebook</Text>
</View>
</Pressable>
<Pressable
style={{backgroundColor: selectedAudio ? 'white' : 'transparent'}}>
<View style={styles.btn}>
<Text>Audiobook</Text>
</View>
</Pressable>
</View>
<View style={styles.container}>
<FlatList
data={books}
keyExtractor={item => item.id?.toString()}
renderItem={renderBooks}
numColumns={2}
/>
</View>
</ScrollView>
);
}
here is my code in BookBox component:
import {Text, Image, StyleSheet, View, TouchableHighlight} from 'react-native';
import React from 'react';
import {Book} from '../types';
import {useNavigation} from '#react-navigation/native';
interface Props {
book: Book;
}
export default function BookBox({book}: Props) {
const {
author,
country,
imageLink,
language,
link,
pages,
title,
year,
overview,
vote,
} = book;
const navigation = useNavigation();
return (
<View style={styles.container}>
{imageLink && (
<TouchableHighlight
onPress={() =>
navigation.navigate('SingleBook', {
title,
year,
pages,
link,
language,
imageLink,
country,
author,
overview,
vote,
})
}>
<Image
style={styles.image}
source={{
uri: `${imageLink}`,
}}
/>
</TouchableHighlight>
)}
{!imageLink && <Text>immagine mancante</Text>}
<View style={styles.wrap}>
<Text style={styles.title}>{title}</Text>
<Text style={styles.title}>{author}</Text>
</View>
</View>
);
}
i'm trying to render items with unique style like in the image above.. can you help me? thank you in avance
So, numColumns of FlatList cannot help you to achieve the behavior you want. This is because masonry are not supported by this property, in other words, numColumns sets the same height for every item at the same line. As a resort to implementing this behavior you can check this lib: https://github.com/hyochan/react-native-masonry-list
I am trying to use Async Storage in react native expo cli to use login credentials even after closing the app. I am using firebase otp authentication as a mode of login. I am struck here to use the previously used phone number and otp. Below is my code.
This is one screen where user will enter mobile number.
import React, {useState, useRef} from 'react'
import { Alert, View } from 'react-native';
import { NativeBaseProvider, Text, Image, Button, Input } from 'native-base';
import { Pressable } from 'react-native';
import { Col, Row, Grid } from "react-native-easy-grid";
import { LinearGradient } from 'expo-linear-gradient';
import styles from '../styles/styles';
import firebase, {phoneAuthProvider} from '../firebase';
import { FirebaseRecaptchaVerifierModal } from 'expo-firebase-recaptcha';
import AsyncStorage from '#react-native-async-storage/async-storage';
const DummyWelcome = ({ navigation }) => {
const [phoneNumber, setPhoneNumber] = useState("");
const [verificationId, setVerificationId] = useState(null);
const recaptchaVerifier = useRef(null);
const sendVerification = async () => {
try {
await AsyncStorage.setItem('phoneNumber', phoneNumber)
}
catch (erorr) {
console.log(error);
}
console.log('Storing phone Number') ;
phoneAuthProvider
.verifyPhoneNumber(phoneNumber, recaptchaVerifier.current)
.then((id) => {
// console.log('verification:',id);
setVerificationId(id);
navigation.navigate("DumOtp", {verificationId: id})
});
};
return (
<NativeBaseProvider>
<FirebaseRecaptchaVerifierModal
ref={recaptchaVerifier}
firebaseConfig={firebase.app().options} />
<Grid >
<LinearGradient
colors={['#64B278', '#03621B']}
style={styles.linearStyle}/>
<Row size={40}
style={styles.containerRow}>
<Image
source={{uri: 'https://d2j6dbq0eux0bg.cloudfront.net/images/30049435/1646525107.jpg'}}
alt="Subhiksha Logo"
width= {150}
height={150}/>
</Row>
<Row size={5}
style={styles.textStyle}>
<Text
style={styles.welcomeText}>Welcome</Text>
</Row>
<Row size={5}
style={styles.textStyle}>
<View>
<Text style={styles.phoneText}>Enter your phone number to contine</Text>
</View>
</Row>
<Row size={10}
style={styles.justifyCenter}>
<Input
style={styles.inputText}
placeholder="Enter your phone Number"
placeholderTextColor= "white"
onChangeText={text => setPhoneNumber(text)}
maxLength={13}
keyboardType='phone-pad'/>
</Row>
<Row size={20}
style={styles.justifyCenter}>
<Pressable
style={styles.pressableStyle}
onPress={sendVerification}>
{/* onPress = {() => */}
{/* text.length < 10 ? Alert.alert("Invalid Submission", "Please Enter 10 digit Mobile Number to continue") : navigation.navigate("DumOtp")}> */}
<Text
style={styles.pressableTextStyle}>Generate OTP</Text>
</Pressable>
</Row>
</Grid>
</NativeBaseProvider>
)
}
export default DummyWelcome
This is another screen where otp will be auto captured.
import React,{ useState, useEffect, useRef } from 'react'
import { NativeBaseProvider, Image, View, Pressable, Text, Button } from 'native-base'
import { Col, Row, Grid } from "react-native-easy-grid";
import { LinearGradient } from 'expo-linear-gradient';
import OTPInputView from '#twotalltotems/react-native-otp-input'
import Clipboard from '#react-native-community/clipboard'
import styles from '../styles/styles';
import AsyncStorage from '#react-native-async-storage/async-storage';
import firebase from '../firebase';
import { FirebaseRecaptchaVerifierModal } from 'expo-firebase-recaptcha';
const DummyOtp = ({ navigation, route }) => {
const [seconds, setSeconds] = useState(60);
const [code, setCode] = useState('');
// const [verificationId, setVerificationId] = useState(null);
const recaptchaVerifier = useRef(null);
const {verificationId} = route.params;
// console.log(code)
useEffect(() => {
if (seconds > 0) {
setTimeout(() => setSeconds(seconds - 1), 1000);
} else {
setSeconds('OTP Expired!');
}
});
const confirmCode = async () => {
try {
const dataa = await AsyncStorage.getItem('phoneNumber');
if(dataa!== null){
const credential = firebase.auth.PhoneAuthProvider.credential(
verificationId,
code
);
firebase
.auth()
.signInWithCredential(credential)
.then((result) => {
// console.log(result);
navigation.navigate("DumQuestion1");
});
}
}
catch (error) {
console.log(error)
}
}
return (
<NativeBaseProvider>
{/* <FirebaseRecaptchaVerifierModal
ref={recaptchaVerifier}
firebaseConfig={firebase.app().options}/> */}
<Grid >
<LinearGradient
colors={['#64B278', '#03621B']}
style={styles.linearStyle}
/>
<Row size={30}
style={styles.justifyCenter}>
<Image
source={{uri: 'https://d2j6dbq0eux0bg.cloudfront.net/images/30049435/1646525107.jpg'}}
alt="Subhiksha Logo"
width= {150}
height={150}
/>
</Row>
<Row size={5}
style={styles.textStyle}>
<Text
style={styles.welcomeText}>Welcome</Text>
</Row>
<Row size={5}
style={styles.textStyle}>
<View>
<Text style={styles.phoneText}>Enter one time password to contine</Text>
</View>
</Row>
<Row size={10}
style={styles.otpRow}>
<OTPInputView
style={styles.otpStyles}
pinCount={6}
autoFocusOnLoad
codeInputFieldStyle={styles.codeInputFieldStyle}
codeInputHighlightStyle={styles.codeInputHighlightStyle}
onChangeText={text => setCode(text)}
onCodeFilled = {(code => {
setCode(code);
})}
/>
</Row>
<Row size={10}
style={styles.centerContainer}>
<Text style={styles.otpTimerText}>{seconds}</Text>
</Row>
<Row size={10}
style={styles.centerContainer}>
<Text underline style={styles.otpNotRecieved}>Didn't receive OTP?</Text>
<Text style={styles.resendText}> Resend</Text>
</Row>
<Row size={15}
style={{alignItems: 'flex-start', justifyContent: 'center', marginTop: 10}}>
<Pressable
style={styles.pressableStyle}
onPress={confirmCode}>
<Text
style={styles.pressableTextStyle}>Authenticate</Text>
</Pressable>
</Row>
{/* <Row size={15}
style={{alignItems: 'flex-start', justifyContent: 'center', marginTop: 10}}>
<Pressable
style={styles.pressableStyle}
onPress={() => console.log(verificationId)}>
<Text
style={styles.pressableTextStyle}>Login</Text>
</Pressable>
</Row> */}
</Grid>
</NativeBaseProvider>
)
}
export default DummyOtp;
im relative new to react native and firebase and it would be awesome if anyone could help me with this problem. currently when im adding new posts to my firebase collection i display all post with a flatlist and it works fine. but is it possible to get only the currentLatitude and currentLongitude for my markers? my target is to generate a new marker for each post.
Events = []
this.firestore.collection("Events").get().then(snapshot => {
snapshot.forEach(doc => {
Events.push(doc.data())
})
})
render() {
return (
<SafeAreaView style={{ flex: 1 }}>
<View style={{ flex: 1 }}>
<MapView
provider={PROVIDER_GOOGLE}
mapType='hybrid'
showsUserLocation style={{flex: 1}}>
<MapView.Marker
coordinate={{latitude: //currentLatitude,
longitude: //currntLongitude}}
title={("Test")}
description={("Test")}
/>
</MapView>
</View>
</SafeAreaView>
);
}
}
#DevAS thanks for your patience.. this was made from 3 different .js files.. but I don't now how to get it just into the map.js.
The final result should look something like this:
enter image description here
Everything except for the lat/lng cords are supposed to be in the callout-window.
Item.js:
import { Ionicons } from '#expo/vector-icons';
import React from 'react';
import { Image, StyleSheet, Text, View } from 'react-native';
import Fire from '../screens/Fire'
const profileImageSize = 36;
const padding = 12;
export default class Item extends React.Component {
state = {
user: {}
};
componentDidMount() {
const user = this.props.uid || Fire.shared.uid;
this.unsubscribe = Fire.shared.firestore
.collection("users")
.doc(user)
.onSnapshot(doc => {
this.setState({ user: doc.data() });
});
if (!this.props.imageWidth) {
// Get the size of the web image
Image.getSize(this.props.image, (width, height) => {
this.setState({ width, height });
});
}
}
componentWillUnmount() {
this.unsubscribe();
}
render() {
const { title, address, name, imageWidth, imageHeight, image, currentLatitude, currentLongitude } = this.props;
// Reduce the name to something
const imgW = imageWidth || this.state.width;
const imgH = imageHeight || this.state.height;
const aspect = imgW / imgH || 1;
return (
<View>
<Header image={{ uri: this.state.user.avatar }} name={this.state.user.name} />
<Image
resizeMode="contain"
style={{
backgroundColor: "#D8D8D8",
width: "100%",
aspectRatio: aspect
}}
source={{ uri: image }}
/>
<Metadata
name={this.state.user.name}
address={address}
title={title}
currentLongitude={currentLongitude}
currentLatitude={currentLatitude}
/>
</View>
);
}
}
const Metadata = ({ name, address, title, currentLongitude, currentLatitude}) => (
<View style={styles.padding}>
<IconBar />
<Text style={styles.text}>{name}</Text>
<Text style={styles.subtitle}>{address}</Text>
<Text style={styles.subtitle}>{title}</Text>
<Text style={styles.subtitle}>Lat: {currentLatitude}</Text>
<Text style={styles.subtitle}>Lng: {currentLongitude}</Text>
</View>
);
const Header = ({ name, image }) => (
<View style={[styles.row, styles.padding]}>
<View style={styles.row}>
<Image style={styles.avatar} source={image} />
<Text style={styles.text}>{name}</Text>
</View>
<Icon name="ios-more" />
</View>
);
const Icon = ({ name }) => (
<Ionicons style={{ marginRight: 8 }} name={name} size={26} color="black" />
);
const IconBar = () => (
<View style={styles.row}>
<View style={styles.row}>
<Icon name="ios-heart-empty" />
<Icon name="ios-chatbubbles" />
<Icon name="ios-send"/>
</View>
<Icon name="ios-bookmark" />
</View>
);
const styles = StyleSheet.create({
text: { fontWeight: "600" },
subtitle: {
opacity: 0.8
},
row: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center"
},
padding: {
padding
},
avatar: {
aspectRatio: 1,
backgroundColor: "#D8D8D8",
borderWidth: StyleSheet.hairlineWidth,
borderColor: "#979797",
borderRadius: profileImageSize / 2,
width: profileImageSize,
height: profileImageSize,
resizeMode: "cover",
marginRight: padding
}
});
List.js
import React from 'react';
import { FlatList } from 'react-native';
import Footer from './Footer';
import Item from './Item';
class List extends React.Component {
renderItem = ({ item }) => <Item {...item} />;
keyExtractor = item => item.key;
render() {
const { onPressFooter, ...props } = this.props;
return (
<FlatList
keyExtractor={this.keyExtractor}
ListFooterComponent={footerProps => (
<Footer {...footerProps} onPress={onPressFooter} />
)}
renderItem={this.renderItem}
{...props}
/>
);
}
}
export default List;
FeedScreen.js
import firebase from "firebase";
import React, { Component } from "react";
import { LayoutAnimation, RefreshControl } from "react-native";
import List from "../components/List";
import Fire from "./Fire";
// Set the default number of images to load for each pagination.
const PAGE_SIZE = 5;
console.disableYellowBox = true;
export default class FeedScreen extends Component {
state = {
loading: false,
data: {}
};
componentDidMount() {
// Check if we are signed in...
if (Fire.shared.uid) {
// If we are, then we can get the first 5 posts
this.makeRemoteRequest();
} else {
// If we aren't then we should just start observing changes. This will be called when the user signs in
firebase.auth().onAuthStateChanged(user => {
if (user) {
this.makeRemoteRequest();
}
});
}
}
// Append the item to our states `data` prop
addPosts = posts => {
this.setState(previousState => {
let data = {
...previousState.data,
...posts
};
return {
data,
// Sort the data by timestamp
posts: Object.values(data).sort((a, b) => a.timestamp < b.timestamp)
};
});
};
// Call our database and ask for a subset of the user posts
makeRemoteRequest = async lastKey => {
// If we are currently getting posts, then bail out..
if (this.state.loading) {
return;
}
this.setState({ loading: true });
// The data prop will be an array of posts, the cursor will be used for pagination.
const { data, cursor } = await Fire.shared.getPaged({
size: PAGE_SIZE,
start: lastKey
});
this.lastKnownKey = cursor;
// Iteratively add posts
let posts = {};
for (let child of data) {
posts[child.key] = child;
}
this.addPosts(posts);
// Finish loading, this will stop the refreshing animation.
this.setState({ loading: false });
};
// Because we want to get the most recent items, don't pass the cursor back.
// This will make the data base pull the most recent items.
_onRefresh = () => this.makeRemoteRequest();
// If we press the "Load More..." footer then get the next page of posts
onPressFooter = () => this.makeRemoteRequest(this.lastKnownKey);
render() {
// Let's make everything purrty by calling this method which animates layout changes.
LayoutAnimation.easeInEaseOut();
return (
<List
refreshControl={
<RefreshControl
refreshing={this.state.loading}
onRefresh={this._onRefresh}
/>
}
onPressFooter={this.onPressFooter}
data={this.state.posts}
/>
);
}
}
I'm trying when I press Login button to go to Drawer screen which contains all my other screens but I'm getting the following error:
TypeError: undefined is not an object (evaluating
'this.props.navigation.navigate')
Here is my DrawerScreen:
import React, {Component} from 'react';
import { Button, View, Text, Dimensions, ImageBackground } from 'react-native';
import { createStackNavigator, createAppContainer, createDrawerNavigator, DrawerItems } from 'react-navigation';
import HomeScreen from './HomeScreen';
import AboutScreen from './AboutScreen';
import Count from './CountScreen';
const DrawerContent = (props) => (
<View>
<ImageBackground
source={{uri:'https://cdn.pixabay.com/photo/2017/12/13/07/15/natural-3016177_960_720.jpg'}}
style={{}}
imageStyle= {{opacity: 0.7}}
>
<View
style={{
//backgroundColor: '#73a2ef',
height: 140,
alignItems: 'center',
justifyContent: 'center',
}}
>
<Text style={{ color: 'black', fontSize: 40, fontWeight: 'bold' }}>
Menu
</Text>
</View>
</ImageBackground>
<DrawerItems {...props} />
</View>
)
const WIDTF = Dimensions.get('window').width;
const DrawerConfig = {
drawerWidth: WIDTF*0.80,
drawertType: 'back',
}
const Drawer = createDrawerNavigator ({
Home: {
screen: HomeScreen
},
About: {
screen: AboutScreen
},
Count: {
screen: Count
},
},
{
drawerBackgroundColor: '#c7d1a7',
contentComponent: DrawerContent,
contentOptions: {
activeTintColor: 'blue',
style: {
flex: 1,
paddingTop: 15,
}
}},
DrawerConfig
);
const AppContainer = createAppContainer(Drawer);
export default AppContainer;
And here is my LogInScreen:
import React, {Component} from 'react';
import * as firebase from 'firebase';
import {Container, Content, Header, Form, Input, Item, Button, Label, Drawer} from 'native-base';
import {StyleSheet, Text} from 'react-native';
import AppContainer from './DrawerNavigatorNew';
const firebaseConfig = {
apiKey:
authDomain:
databaseURL:
projectId:
storageBucket:
};
firebase.initializeApp(firebaseConfig);
export default class LoginScreen extends React.Component {
constructor (props) {
super(props)
this.state =({
email:'',
password:'',
})
}
signUpUser = (email, password) => {
try {
if(this.state.password.length <6)
{
alert("Please enter atleast 6 characters")
return;
}
firebase.auth().createUserWithEmailAndPassword(email, password)
}
catch (error){
console.log(error.toString())
}
}
loginUser =(email, password) => {
try{
firebase.auth().signInWithEmailAndPassword(email, password).then(function(user){
console.log(user)
})
}
catch (error) {
console.log(error.toString())
}
}
render() {
const {navigate} = this.props.navigation;
return (
<Container style={styles.container}>
<Form>
<Item floatingLabel>
<Label> Email </Label>
<Input
name='email'
autoCorrect={false}
autoCapitalize='none'
onChangeText={(email)=> this.setState({email})}
/>
</Item>
<Item floatingLabel>
<Label> Password </Label>
<Input
name='password'
secureTextEntry={true}
autoCorrect={false}
autoCapitalize='none'
onChangeText={(password)=> this.setState({password})}
/>
</Item>
<Button style={{marginTop: 10}}
full
rounded
success
onPress = {()=> this.loginUser(this.state.email,this.state.password) || navigate(AppContainer)}
>
<Text> Login </Text>
</Button>
<Button style={{marginTop: 10}}
full
rounded
primary
onPress = {()=> this.signUpUser(this.state.email,this.state.password)}
>
<Text style={{color: 'white' }}> Sign Up </Text>
</Button>
</Form>
</Container>
);
}
}
const styles = StyleSheet.create ({
container:{
flex: 1,
backgroundColor: 'white',
justifyContent: 'center',
padding: 10
}
})
And the Error:
I misread the code. 'Navigate' is a command to move the screen. The 'createDrawenavigator' must open the drawer.
Can you try this Code?
onPress = {()=> this.loginUser(this.state.email,this.state.password) || this.props.navigation.openDrawer();}
Change the Navigator configuration if you want to move the screen.
createStackNavigator({
A: {
screen: AScreen,
navigationOptions: () => ({
title: `A`,
headerBackTitle: 'A much too long text for back button from B to A',
headerTruncatedBackTitle: `to A`
}),
},
B: {
screen: BScreen,
navigationOptions: () => ({
title: `B`,
}),
}
});
I am using RN vector icon for bookmark purpose.
Current behavior:
I have multiple photos with icon on it, when i clicked on any icon of any photo all icon's color get changed.
Expected behavior:
When i press any icon, only that icon's color should be changed, not all.
How should i achieve it?
i have following code:
this.state = {
toggle: false,
}
<TouchableHighlight>
<Icon name="ios-ribbon" onPress={()=> this.setState({toggle: !this.state.toggle})} size={30} style={[styles.icon, this.state.toggle && styles.iconAlt]} />
</TouchableHighlight>
style
icon: {
color: '#000',
height: 40,
marginLeft: 60,
marginRight: 60,
marginBottom: 30,
padding: 2,
},
iconAlt: {
color: '#ff6600',
},
This is normal condition i.e before clicked on any icon
After clicked on any single icon, All icons color get changed
Well, this seems like a question about changing the color of the icon, but really beyond that in my opinion.
Let's say each product has only a title and an image, these 2 states. The color of the icon indicates whether a user has added this product to his/her favorite, therefore, a product should also have a state called favorite, which is boolean. Since each product has different states, let
's make a PureComponent called Product like so:
import React, { PureComponent } from 'react';
import { View, Image, Text, LayoutAnimation } from 'react-native';
import Icon from 'react-native-vector-icons/FontAwesome';
class Product extends PureComponent {
constructor(props) {
super(props);
this.state = {
title: '',
image: '',
favorite: false
};
}
componentWillMount() {
const { title, image, favorite } = this.props;
this.setState({ title, image, favorite });
}
componentWillUpdate() {
LayoutAnimation.easeInEaseOut();
}
render() {
const { title, image, favorite } = this.state;
return (
<View style={{ ... }}>
<Text style={{ ... }}>
{title}
</Text>
<Image
source={image}
style={{ ... }}
/>
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<Icon
name={favorite ? 'heart' : 'heart-o'}
color={favorite ? '#F44336' : 'rgb(50, 50, 50)'}
size={30}
style={{ marginBottom: 10, marginTop: 20 }}
onPress={() => this.setState({ favorite: !favorite })}
/>
<Text style={{ ... }}>
{favorite ? 'Remove from favorite' : 'Add to favorite'}
</Text>
</View>
</View>
);
}
}
export default Product;
And in our main screen, we have an array of products, then we put them in a FlatList:
<FlatList
data={this.state.products}
keyExtractor={(item) => item.title}
renderItem={({ item }) => (
<Product
title={item.title}
image={item.image}
favorite={item.favorite}
/>
)}
/>
Then, you will have this, it looks very ugly, just style it a little bit, bear with me here: