React native Flatlist navigate to diffrent page - react-native

import React from 'react';
import { FlatList, StyleSheet, Text, View , ScrollView ,TouchableHighlight,TouchableOpacity,Touchable} from 'react-native';
import { useState } from 'react';
function HomeContent({navigation}){
const [Level, setLevel] = useState([
{name:'ม.3\nปี59', key:1},
{name:'ม.3\nปี60', key:2},
{name:'ม.3\nปี61', key:3},
{name:'ม.3\nปี62', key:4},
{name:'ม.3\nปี63', key:5},
{name:'ม.6\nปี59', key:6},
{name:'ม.6\nปี60', key:7},
{name:'ม.6\nปี61', key:8},
{name:'ม.6\nปี62', key:9},
{name:'ม.6\nปี63', key:10},
])
const pressHandler = () => {
var i;
for (i = 1; i < 11; i++) {
navigation.navigate(`Exam${i}`) /*exam.{key_page}*/
}
}
return(
<View style = {styles.mainContent}>
<ScrollView showsVerticalScrollIndicator ={false}>
<FlatList
data = {Level}
renderItem = {({item}) => (
<TouchableOpacity onPress= {pressHandler}>
<View style = {styles.mainContentBox} >
<Text style={styles.mainContentBoxText}>{item.name}</Text>
</View>
</TouchableOpacity>
)}
numColumns = {2}
/>
</ScrollView>
</View>
)
}
I have many Exam pages (Exam[1-10]) how to navigate to a different page I try to use for loop the number but it doesn't work that make me go to all page 1-10
I am a newbie sorry if this is obvious

Try this way
const pressHandler = (item) => {
navigation.navigate(`Exam${item.key}`);
}
<TouchableOpacity onPress={() => { pressHandler(item)} >
<View style = {styles.mainContentBox} >
<Text style={styles.mainContentBoxText}>{item.name}</Text>
</View>
</TouchableOpacity>

get index prop from renderItem and navigate
renderItem = {({item,index}) => (
<TouchableOpacity onPress= {() => {
const pageNumber = index+1;
navigation.navigate(`Exam${pageNumber}`) /*exam.{key_page}*/
}}>
.....

Related

Use .map not flatlist with activityIndicator in react native

I am getting a list of data using map not flatlist, I want to use .map not flatlist, when I apply ActivityIndicator to it while fetching the data, it did not work see below
Below is my code
<View>
{dataList.map((dataList, index) =>
<Text key={index} >{dataList.category_name}</Text>
<Text key={index} >{dataList.category_number}</Text>
)}
</View>
When I tried it with ActivityIndicator see below
{ dataList ?
<View>
{dataList.map((dataList, index) =>
<Text key={index} >{dataList.category_name}</Text>
<Text key={index} >{dataList.category_number}</Text>
)}
</View>
:
<ActivityIndicator />
}
It the not work, I will need your help with this.
Thanks in advance
Try using a boolean for your conditional render such as a loading state which you can easily toggle on and off at the beginning of the fetch and at the end respectively. You can also state the logic of your map outside of your component so it looks way cleaner and easy to read like this example:
import React from 'react';
import { Text, View, ActivityIndicator } from 'react-native';
const dataList = [
{ category_name: 'pop', category_number: 1 },
{ category_name: 'dance', category_number: 2 },
{ category_name: 'hiphop', category_number: 3 },
];
const renderDataList = () => {
return dataList.map((dataList, index) => (
<View>
<Text>{dataList.category_name}</Text>
<Text>{dataList.category_number}</Text>
</View>
));
};
const App = () => {
const [isLoading, setIsloading] = React.useState(false);
return <View>{isLoading ? <ActivityIndicator /> : renderDataList()}</View>;
};
export default App;
Your output would be for false:
Your output for true would be:

react native I want to designate one image as the default

I wrote the code to upload an image, but when I click remove image, a blue screen appears as a default. I want to designate this screen as one image.
import 'react-native-gesture-handler'
import * as React from 'react';
import{
StyleSheet,
View,
Text,
Image,
TouchableOpacity,
ToastAndroid,
Alert,
} from 'react-native';
import { Avatar } from 'react-native-paper';
import { launchImageLibrary } from 'react-native-image-picker';
import styled from 'styled-components';
const Container = styled.SafeAreaView`
flex : 1;
`;
export default function Profile(){
const [pic, Setpic] = React.useState('');
const setToastMsg = msg => {
ToastAndroid.showWithGravity(msg, ToastAndroid.SHORT, ToastAndroid.CENTER)
};
const uploadImage = () => {
let options = {
mediaType : 'photo',
quality : 1,
includeBase64 : true,
};
launchImageLibrary(options, response => {
if(response.didCancel){
setToastMsg('Cancelled image selection');
}
else if((response.errorCode == 'permission')){
setToastMsg('permision not satisfied');
}
else if((response.errorCode == 'others')){
setToastMsg(response.errorMessage);
}
else if(response.assets[0].fileSize > 2097152){
Alert.alert(
'Maximum image size exceeded',
'Please choose image under 2MB'
[{text: 'OK'}],
);
}
else{
Setpic(response.assets[0].base64);
}
});
}
const removeImage = () => {
Setpic('');
setToastMsg('Image removed');
}
return(
<Container>
<View>
<Avatar.Image
size= {150}
source={{ uri : 'data:image/png;base64,' + pic}}
/>
</View>
<View>
<TouchableOpacity activeOpacity={0.5} onPress={ ()=> uploadImage() }>
<Text>
Upload Image
</Text>
</TouchableOpacity>
<TouchableOpacity activeOpacity={0.5} onPress={ ()=> removeImage() }>
<Text>
Remove Image
</Text>
</TouchableOpacity>
</View>
</Container>
);
}
Can I change the code here?
const removeImage = () => {
Setpic('');
setToastMsg('Image removed');
}
or here?
<View>
<Avatar.Image
size= {150}
source={{ uri : 'data:image/png;base64,' + pic}}
/>
</View>
I'm curious where you set the default image.
If you can't change the default image, please help me to change the color.
This code is based on Android
you could try a condition if pic is empty or not.
If it is empty will be rendered. If not You Avatar will be shown.
{ pic === "" ? (
<View>
<Text>Screen if pic is empty</Text>
<View>
):
( <View>
<Avatar.Image
size= {150}
source={{ uri : 'data:image/png;base64,' + pic}}
/>
</View>
)

How do I display the numbers 0 to 10 in react native without repetition each time I click on the output?

I want the numbers 0 to 10 to be repeated only once and then start counting from 0 to 10 again.
enter image description here
import React, {Component} from 'react';
import {
StyleSheet,
Text,
View,
StatusBar,
SafeAreaView,
TouchableOpacity,
} from 'react-native';
import Ionicons from 'react-native-vector-icons/Ionicons';
export class CertificationScreen extends Component{
constructor(props) {
super(props);
}
render() {
const count = 0;
return (
<SafeAreaView style={styles.container}>
<StatusBar barStyle="light-content" />
<View style={styles.item}>
<TouchableOpacity onPress={() => this.props.navigation.navigate('Forms', {itemId: count + 1 })}>
<Ionicons style={styles.icons} name="arrow-back" size={24} color="#0064FE" />
<Text style={{fontSize: 24}}>Text</Text>
</TouchableOpacity>
</View>
</SafeAreaView>
);}
}
I researched a lot, but I could not solve this problem.
please guide me.
Yes, this is exactly what I want, to add a number by tapping TouchableOpacity. And send this data to my class component via props (itemId). But after making changes, I get this error: Too many re-renders. React limits the number of renders to prevent an infinite loop. Do you know a better way I can use it?
import React from 'react';
import {
StyleSheet,
Text,
View,
StatusBar,
SafeAreaView,
TouchableOpacity,
} from 'react-native';
import Ionicons from 'react-native-vector-icons/Ionicons';
export function CertificationScreen() {
let [counter, setCounter] = React.useState();
if (counter < 10) {
setCounter(prev => prev + 1);
} else {
setCounter(0);
}
return (
<SafeAreaView style={styles.container}>
<StatusBar barStyle="light-content" />
<View style={styles.item}>
<TouchableOpacity onPress={() =>
navigation.navigate('Forms', {itemId: setCounter })}>
<Ionicons style={styles.icons} name="arrow-back" size={24} color="#0064FE" />
<Text style={{fontSize: 24}}>Text</Text>
</TouchableOpacity>
</View>
</SafeAreaView>
);
}
enter image description here
This is exactly what I want:
let [counter, setCounter] = React.useState ();
if (counter <10) {
setCounter (prev => prev + 1);
} else {
setCounter (0);
}
After clicking on touchableOpacity, I want the data from the counter to be transferred via prop and navigation to the next page where the data is displayed in .
enter image description here
I created a snack example to better understand this issue, please let me know if there is an error in the process.
https://snack.expo.dev/#devsaeedhabibi/certification
Update:
How can I integrate Button and TouchableOpacity?
<Button title='Certification' onPress={() => {
if(counter < 10) {
setCounter(prev => prev + 1)
}else {
setCounter(1)
}
}} />
<TouchableOpacity onPress={() => navigation.navigate('Forms', {itemId: counter })}>
<Ionicons style={styles.icons} name="arrow-back" size={24} color="#0064FE" />
<Text style={{fontSize: 24, alignSelf: 'center'}}>Certification</Text>
</TouchableOpacity>
Take a look at this snack:
https://snack.expo.dev/#devsaeedhabibi/certification-2
Edit:
Using a Pressable component solved the problem.
<Pressable onPressIn={()=> {if(counter < 10) {setCounter(prev => prev + 1)}else{setCounter(1)}}}
onPress={() => navigation.navigate('Forms', {itemId: counter })}>
onPressIn:
Called immediately when a touch is engaged, before onPressOut and onPress.
onPressOut:
Called when a touch is released.
onPress:
Called after onPressOut.
This is exactly what I wanted.
Take a look at this snack:
https://snack.expo.dev/#devsaeedhabibi/certification-v3
Thank you for your attention to this matter.
I think this is what you want;
import React, {useState} from 'react'
import {View, Text, Button} from 'react-native'
const App = () => {
let [counter, setCounter] = useState(0)
return (
<View>
<Button title='counter' onPress={() => {
if (counter < 10) {
setCounter(prev => prev + 1)
} else {
setCounter(0)
}
}}/>
<Text>{counter}</Text>
</View>
)
}
export default App;

React Native. Flatlist order changes when updating item

Click here for Example
Hello all.
I have a small issue with a React Native Flatlist.
It takes the items from Redux store, but when i try to update the amount, with redux as well it changes me the order of the list. Except in last item.
If you encountered similar issue let me know.
Thanks in advance
So far i discovered that the increase and decrease functions in Cart Item changes the order of my array.
These are the components i'm using to render the list.
CART LIST
import React from "react";
import { FlatList } from "react-native";
import CartItem from "./CartItem";
const CartList = ({ data }) => {
return (
<React.Fragment>
{data && (
<FlatList
showsVerticalScrollIndicator={false}
data={data}
renderItem={({ item }) => <CartItem item={item} />}
keyExtractor={(item) => console.log(item)}
/>
)}
</React.Fragment>
);
};
export default CartList;
CART ITEM
import React, { useCallback } from "react";
import { View, Text, StyleSheet, Image } from "react-native";
import IncreaseDecrease from "./IncreaseDecrease";
// Theme
import { Theme } from "../../Theme";
const colors = Theme.colors;
// Redux
import { useDispatch, useSelector } from "react-redux";
import {
updateQuantity,
removeFromCart,
} from "../../Redux/Actions/cartActions";
const CartItem = React.memo(({ item }) => {
const dispatch = useDispatch();
const { cart } = useSelector((state) => ({
cart: state.cart.cart,
}));
const increaseQuantity = useCallback(
() => {
let currentProduct = cart.filter((el) => el.product.id === item.product.id);
let quantity = currentProduct[0].quantity + 1;
dispatch(updateQuantity(item.product.id, quantity));
},
[],
);
const decreaseQuantity = useCallback(
() => {
let currentProduct = cart.filter((el) => el.product.id === item.product.id);
let quantity = currentProduct[0].quantity - 1;
if (quantity == 0) {
dispatch(removeFromCart(item.product.id));
} else {
dispatch(updateQuantity(item.product.id, quantity));
}
},
[],
);
return (
<React.Fragment>
{item && (
<View style={styles.container}>
<View style={styles.imageContainer}>
<Image style={styles.image} source={{ uri: item.product.image }} />
</View>
<View style={styles.textContainer}>
<Text
style={[styles.text, { fontSize: 16, color: colors.darkGrey }]}
>
{item.product.name}
</Text>
</View>
<View style={styles.quantity}>
<IncreaseDecrease
quantity={item.quantity}
increase={increaseQuantity}
decrease={decreaseQuantity}
/>
</View>
<View style={styles.priceContainer}>
<Text
style={[styles.text, { fontSize: 16, color: colors.darkGrey }]}
>
${item.product.price * item.quantity}
</Text>
</View>
</View>
)}
</React.Fragment>
);
});
INCREASE DECREASE
import React from "react";
import { View, StyleSheet, Text } from "react-native";
import PropTypes from "prop-types";
import Icon from "react-native-vector-icons/Ionicons";
// Theme
import { Theme } from "../../Theme";
import { TouchableOpacity } from "react-native";
const colors = Theme.colors;
// Types
const propTypes = {
quantity: PropTypes.number,
increase: PropTypes.func,
decrease: PropTypes.func,
};
// Default Props
const defaultProps = {
quantity: 0,
};
const IncreaseDecrease = ({ quantity, increase, decrease }) => {
return (
<View style={styles.container}>
<TouchableOpacity onPress={decrease}>
<View style={styles.button}>
<Icon name={"remove"} size={20} color={colors.white} />
</View>
</TouchableOpacity>
<View style={styles.input}>
<Text>{quantity}</Text>
</View>
<TouchableOpacity onPress={increase}>
<View style={styles.button}>
<Icon name={"add"} size={20} color={colors.white} />
</View>
</TouchableOpacity>
</View>
);
};
REDUCER:
case UPDATE_QUANTITY:
let item = cart.find(
(item) => item.product.id == action.payload.productId
);
let newCart = cart.filter(
(item) => item.product.id != action.payload.productId
);
item.quantity = action.payload.quantity;
newCart.push(item);
return {
...state,
cart: newCart,
};
Okay i got it.
The issue was in my reducer, i was pushing a new updated element to a new array.
The solution is to change the element and update through the index.
Here is my solution:
REDUCER:
case UPDATE_QUANTITY:
let item = cart.find(
(item) => item.product.id == action.payload.productId
);
let index = cart.indexOf(item)
item.quantity = action.payload.quantity;
let newCart = cart.slice()
newCart[index] = item
return {
...state,
cart: newCart,
};
In your reducer what you are doing is finding the item whose quantity is to be updated. Update its quantity and push that item to the end of the list.
Hence changing the order of items in the cart.
Instead, you should try to find the index of the item to be updated, replace the quantity property in the cart object at that index with a new one and the order of the list will be preserved.
Try this -
case UPDATE_QUANTITY:
let itemIndex = cart.findIndex(
(item) => item.product.id == action.payload.productId
);
let newCart = [...state.cart];
newCart[itemIndex].quantity = action.payload.quantity;
return {
...state,
cart: newCart,
};

React Native inputText with Flatlist

I'm new to react-native. This component below should render comments posted by users, I would like to add an inputText component from react-native to allow users to post a comment, but don't know where I should place it within the code below.
import React, { useState, useEffect } from 'react';
import { useNavigation } from "#react-navigation/native";
import Icon from 'react-native-vector-icons/AntDesign';
import {
StyleSheet,
Text,
View,
TouchableOpacity,
Image,
ScrollView,
FlatList,
Button,
TextInput
} from 'react-native';
import parseDate from "../utils/parseDate";
import * as API from "../api/api"
export default function CommentList({ ride }) {
const [text, setText] = React.useState("");
const [commentsData, setComments] = useState([]);
const navigation = useNavigation();
useEffect(() => {
API.getCommentsByRideId(ride.ride_id).then((comments) => {
setComments(comments)
})
}, [ride.ride_id])
deleteComment = (comment_id) => {
// useEffect(() => {
API.deleteCommentsByCommentId(comment_id).then(() => {
const updatedComments = list.filter((item) => item.comment_id !== comment_id);
setComments(updatedComments)
})
// }, [comment_id])
}
//deletes on refresh only
addComment = (newComment, ride_id) => {
API.postCommentByRideId(newComment, ride_id).then(() => {
setComments(newComment)
})
}
return (
<FlatList
style={styles.root}
data={commentsData}
ItemSeparatorComponent={() => {
return (
<View style={styles.separator} />
)
}}
keyExtractor={(item) => {
return item.author;
}}
renderItem={(item) => {
const comment = item.item;
return (
<View style={styles.container}>
<TouchableOpacity onPress={() => navigation.navigate("UserProfile", { username: item.author })}>
{/* <Image style={styles.image} source={{ uri: comment.avatar_url }} /> */}
</TouchableOpacity>
<View style={styles.content}>
<View style={styles.contentHeader}>
<Text style={styles.name}>{comment.author}</Text>
<Text style={styles.time}>
{parseDate(comment.created_at)}
{comment.votes}
</Text>
</View>
<Text rkType='primary3 mediumLine'>{comment.body}</Text>
{/* <Text style={styles.time}> Likes: {comment.votes}</Text> */}
<TouchableOpacity onPress={() => deleteComment(comment.comment_id)}>
<Icon name="delete" size={20} color="#e33057" />
</TouchableOpacity>
</View>
</View>
);
}} />
);
}
This is the inputText I would like to add to allow users to post a comment.
<TextInput
value={text}
placeholder="write..."
onChangeText={text => setText(text)}
onSubmitEditing={() => addcomment(text, ride.ride_id)}
/>
if you want to add it at fixed position in bottom of screen you may do this
<View style={{flex : 1}}>
<Flatlist
contentContainerStyle={{paddingBottom: 50}}
.../>
<View style={{position : 'absolute', bottom : 0, width : '100%', height : 50}}>
//you input here
</View>
</View>
or if you want to add it last item of flatlist you may use ListFooterComponent
<FlatList
ListFooterComponent={<Input/>}
.../>
</FlatList>