Retrieve a list of products and display them - react-native

I am trying to retrieve a list of products from my API. For the moment, nothing is displayed, the page is currently empty except for my back button and I do not understand why. The two functions I use are functional since both console.log works. Could you help me ?
Everything looks fine, the console.log work in the terminal but I can't display anything in the app.
I tried this snack : https://snack.expo.io/O4oPj8-Qz
const Item = ({ item, onPress, style }) => (
<TouchableOpacity onPress={onPress} style={[styles.productItem, style]}>
<Text style={[styles.h4, {textAlign: "left"}]}>
{item.name}
</Text>
</TouchableOpacity>
);
export default class Products extends Component {
constructor(props) {
super(props);
this.state = {
selectedId: '',
setSelectedId: '',
listData: '',
currentPage: 1,
loadMoreVisible: true,
loadMoreVisibleAtEnd: false,
displayArray: null
}
};
initListData = async () => {
let list = await getProducts(1);
console.log(list)
if (list) {
this.setState({
displayArray: list,
loadMoreVisible: (list.length >= 15 ? true : false),
currentPage: 2
});
}
};
setNewData = async (page) => {
let list = await getProducts(parseInt(page));
if (list) {
this.setState({
displayArray: this.state.displayArray.concat(list),
loadMoreVisible: (list.length >= 15 ? true : false),
loadMoreVisibleAtEnd: false,
currentPage: parseInt(page)+1
});
}
};
loadMore() {
this.setNewData(this.state.currentPage);
}
displayBtnLoadMore() {
this.setState({
loadMoreVisibleAtEnd: true
});
}
async UNSAFE_componentWillMount() {
this.initListData();
}
render() {
return (
<View>
{this.state.displayArray !== null && this.state.displayArray.length > 0 ? (
<View style={{ flex: 1, marginBottom: 100 }}>
<SafeAreaView style={styles.container}>
<FlatList
data={this.state.displayArray}
extraData={this.selectedId}
onEndReached={() => this.displayBtnLoadMore()}
renderItem={({item})=>
<View style={{flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center'}}>
<Item
item={item}
onPress={() => this.props.navigation.navigate('ProductDetails', {productId: parseInt(item.id)})}
/>
</View>
}
keyExtractor={item => "product-" + item.id.toString()}
style={{width:"90%"}}
/>
{this.state.loadMoreVisible === true && this.state.loadMoreVisibleAtEnd === true ? (
<Button title=" + " onPress={()=>{this.loadMore()}}></Button>
) : null
}
<View style={styles.container}>
<Text>{"\n"}</Text>
<TouchableOpacity
style={styles.touchable2}
onPress={() => this.props.navigation.goBack()}
>
<View style={styles.view2}>
<Text style={styles.textimg2}>
back
</Text>
</View>
</TouchableOpacity>
</View>
<Text>{"\n\n"}</Text>
</SafeAreaView>
</View>
) : (
<View style={styles.container}>
<Text>{"\n\n" + (this.state.displayArray === null ? i18n.t("products.searching") : i18n.t("products.nodata")) + "\n\n\n"}</Text>
<TouchableOpacity
style={styles.touchable2}
onPress={() => this.props.navigation.goBack()}
>
<View style={styles.view2}>
<Text style={styles.textimg2}>
Back
</Text>
</View>
</TouchableOpacity>
</View>
)}
</View>
);
};
}

You were not adding flex: 1 and also not calling the API in the right way, here is the snack with the solution.
Link to snack

Thanks to your answers and help. I got it work this way : Thank you so much for yout time and help, really, sincerely.
import React, { Component } from "react";
import { FlatList, SafeAreaView, Button, Text, View, TouchableOpacity } from 'react-native';
import { getProducts } from '../../../src/common/Preferences';
import styles from '../../../assets/styles';
import i18n from '../../../src/i18n';
const Item = ({ item, onPress, style }) => (
<TouchableOpacity onPress={onPress} style={[styles.productItem, style]}>
<Text style={[styles.h4, {textAlign: "left"}]}>
{item.name}
</Text>
</TouchableOpacity>
);
export default class Products extends Component {
constructor(props) {
super(props);
this.state = {
selectedId: '',
setSelectedId: '',
listData: '',
currentPage: 1,
loadMoreVisible: true,
loadMoreVisibleAtEnd: false,
displayArray: []
}
};
initListData = async () => {
let list = await getProducts(1);
if (list) {
this.setState({
displayArray: list,
loadMoreVisible: (list.length >= 15 ? true : false),
currentPage: 2
});
console.log(this.state.displayArray, 'dans initListData')
}
};
setNewData = async (page) => {
let list = await getProducts(parseInt(page));
if (list) {
this.setState({
displayArray: this.state.displayArray.concat(list),
loadMoreVisible: (list.length >= 15 ? true : false),
loadMoreVisibleAtEnd: false,
currentPage: parseInt(page)+1
});
}
};
loadMore() {
this.setNewData(this.state.currentPage);
}
displayBtnLoadMore() {
this.setState({
loadMoreVisibleAtEnd: true
});
}
async UNSAFE_componentWillMount() {
this.initListData();
console.log(this.state.displayArray, 'dans componentWillMount')
}
render() {
console.log('displayArray', this.state.displayArray)
return (
<View style={{flex: 1}}>
<Text>{"\n"}</Text>
<Text>{"\n"}</Text>
{this.state.displayArray !== null && this.state.displayArray.length > 0 ? (
<View style={{ flex: 1, marginBottom: 100 }}>
<SafeAreaView style={styles.container}>
<FlatList
data={this.state.displayArray}
//extraData={this.selectedId}
//onEndReached={() => this.displayBtnLoadMore()}
renderItem={({item})=>
<View style={{flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center'}}>
<Item
item={item}
onPress={() => this.props.navigation.navigate('ProductDetails', {productId: parseInt(item.id)})}
/>
</View>
}
keyExtractor={item => "product-" + item.id.toString()}
style={{width:"90%"}}
/>
{this.state.loadMoreVisible === true && this.state.loadMoreVisibleAtEnd === true ? (
<Button title=" + " onPress={()=>{this.loadMore()}}></Button>
) : null
}
<View style={styles.container}>
<Text>{"\n"}</Text>
<TouchableOpacity
style={styles.touchable2}
onPress={() => this.props.navigation.goBack()}
>
<View style={styles.container}>
<Button
color="#F78400"
title= 'Back'
onPress={() => this.props.navigation.goBack()}>BACK
</Button>
</View>
</TouchableOpacity>
</View>
<Text>{"\n\n"}</Text>
</SafeAreaView>
</View>
) : (
<View style={styles.container}>
<Text>{"\n\n" + (this.state.displayArray === null ? i18n.t("products.searching") : i18n.t("products.nodata")) + "\n\n\n"}</Text>
<Button
color="#F78400"
title= 'Back'
onPress={() => this.props.navigation.goBack()}>BACK
</Button>
</View>
)}
</View>
);
};
}

Related

React native WARN Possible Unhandled Promise Rejection (id: 2): Error: [AsyncStorage] Passing null/undefined as value is not supported

In react native cli i was trying to login but not working nd same thing working fine in expo nd this error is showing please let me know what can i do to do this???? i have added the action ,reducer,component page
getting error this //
WARN Possible Unhandled Promise Rejection (id: 2):
Error: [AsyncStorage] Passing null/undefined as value is not supported. If you want to remove value, Use .removeItem method instead.
Passed value: undefined
Passed key: userToken
checkValidInput#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.easylab&modulesOnly=false&runModule=true:146791:24
http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.easylab&modulesOnly=false&runModule=true:146835:24..........................
my action is like this // actions>index.js page
export const login = (formValues, actions) => {
return async dispatch => {
dispatch(startSubmitting());
const url = `/auth/login`;
var formdata = new FormData();
formdata.append('email', formValues.email);
formdata.append('password', formValues.password);
const response = await api
.post(url, formdata)
.then(res => {
return res;
})
.catch(error => {
actions.setErrors(error.response.data.error);
return error.response;
});
dispatch({
type: 'LOGIN',
payload: response,
});
dispatch(stopSubmitting());
await AsyncStorage.setItem('userToken', response.data.access_token);
};
};
//my component page login.js
import { StatusBar } from "expo-status-bar";
import React, { useState, useEffect } from "react";
import {
Field,
Form,
Formik,
FormikProps,
ErrorMessage,
useFormik,
} from "formik";
import { connect } from "react-redux";
import { login } from "../../../actions";
import { Button } from "react-native-paper";
import ErrorMsg from "./ErrorMsg";
class Login extends React.Component {
submitLogin = (values, actions) => {
this.props.login(values, actions);
};
render() {
const { onChangeText, text, navigation } = this.props;
const { isSubmitting ,isLoading} = this.props.commonData;
const { login, loginLoading, isLoggedIn } = this.props.loginForm;
{
login && this.props.navigation.navigate("Nav1");
}
return (
<KeyboardAvoidingView
behavior={Platform.OS === "ios" ? "padding" : "height"}
style={styles.container}
>
{/* {verifyOtp ? <Loader loading={verifyOtp} /> : null} */}
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<View style={styles.inner}>
<View style={{ alignItems: "center" }}>
<Image
source={require("../../../assets/images/diabetes-awareness-month-1440x810.jpg")}
style={{
height: 110,
// top: -20,
resizeMode: "contain",
}}
/>
</View>
<View style={{}}>
<Text onPress={() => this.props.navigation.navigate("Register")}>
New User ? Register
</Text>
</View>
<View style={{}}>
<Text style={{ fontSize: 24, fontWeight: "bold" }}>LOGIN</Text>
</View>
<View style={{}}>
<Formik
initialValues={{
email: "",
password: "",
}}
validate={(values) => {
const error = {};
if (!values.password) {
error.password = (
<Text style={{ color: "red", fontSize: 10 }}>
Password Required
</Text>
);
}
if (!values.email) {
error.email = (
<Text style={{ color: "red", fontSize: 10 }}>
Email Type Required
</Text>
);
}
return error;
}}
onSubmit={(values, actions) => {
this.submitLogin(values, actions);
}}
enableReinitialize={true}
>
{(props: FormikProps<any>) => (
<>
<View style={{ paddingBottom: 10 }}>
<Text>Login To Check Your Account</Text>
</View>
<View>
{/* email */}
<View style={{ marginBottom: 20 }}>
<View style={styles.textInputContainer}>
<MaterialCommunityIcons
style={{ alignSelf: "center", paddingLeft: 10 }}
name="email"
size={28}
/>
<TextInput
style={styles.input}
onChangeText={props.handleChange("email")}
value={props.values.email}
autoFocus={true}
placeholder="Email"
/>
</View>
</View>
<View style={{ marginBottom: 20 }}>
<View style={styles.textInputContainer}>
<MaterialCommunityIcons
style={{ alignSelf: "center", paddingLeft: 10 }}
name="security"
size={28}
/>
<TextInput
style={styles.input}
onChangeText={props.handleChange("password")}
maxLength={10}
secureTextEntry={true}
value={props.values.password}
placeholder="Password"
/>
</View>
</View>
{/* {props.touched.password && props.errors.password && ( */}
<ErrorMsg msg={props.errors.password} />
{/* )} */}
<TouchableHighlight
underlayColor="white"
onPress={props.handleSubmit}
>
<Text color="white" style={styles.buttonStyle}>
{/* {isSubmitting ? (
<ActivityIndicator
size="small"
color="#4DB2F8"
/>
) : ( */}
<Text>LOGIN </Text>
{/* )} */}
</Text>
</TouchableHighlight>
</View>
</>
)}
</Formik>
</View>
</View>
</TouchableWithoutFeedback>
</KeyboardAvoidingView>
);
}
}
const mapStateToProps = (state) => {
return {
loginForm: state.loginData,
commonData: state.commonFunctions,
};
};
export default connect(mapStateToProps, { login })(Login);
and in redux trying to store like this// reducers>index.js page
const loginReducer = (
state = {
otp: false,
mobile: null,
login: false,
loginLoading: true,
verifyOtp: false,
isLoggedIn: false,
},
action
) => {
switch (action.type) {
case "LOGIN": {
if (action.payload.status === 200) {
let newState = { ...state, login: true, isLoggedIn: true };
return newState;
} else {
let newState = { ...state, login: false, isLoggedIn: false };
return newState;
}
}
default:
return state;
}
};
export default combineReducers({
loginData: loginReducer,
});
Your problem is that you are trying to set userToken to null on this line:
await AsyncStorage.setItem('userToken', response.data.access_token);
You need to do a null check before setting the token, since Async Storage does not support setting null values. Try:
if (response && response.data && response.data.access_token) {
await AsyncStorage.setItem('userToken', response.data.access_token);
}

React Native CRUD with RESTful API & Redux state management

I'm new in react native and trying to create an app with CRUD operation & RESTful API, but I'm stuck in the UPDATE/PUT, Can anyone help in this matter?? Below are my frontend + backend codes:
Backend side
// update data by id
router.put('/:id', validate, (req, res) => {
const bookId = req.params.id
const errors = validationResult(req)
if(!errors.isEmpty()){
return res.status(422).send({errors: errors.array()})
}
Book.findById(bookId)
.then(book => {
book.bookTitle = req.body.bookTitle,
book.ImgURL = req.body.ImgURL,
book.bookDescription = req.body.bookDescription,
book.bookAuthor = req.body.bookAuthor,
book.bookPrice = req.body.bookPrice,
book.bookTypes = req.body.bookTypes,
book.bookYear = req.body.bookYear,
book.bookRating = req.body.bookRating,
book.bookPages = req.body.bookPages
return book.save()
})
.then(result => res.send(result))
.catch(errors => console.log(errors))
})
Result in POSTMAN, I changed title from how to brew -> how to brew like professional
ReduxAction.js
export const editBook = ({id, bookTitle, ImgURL, bookDescription, bookAuthor, bookPrice, bookTypes, bookYear, bookRating, bookPages}) => {
return async dispatch => {
const response = await fetch(`http://localhost:3000/api/books/${id}`, {
method: 'PUT',
body: JSON.stringify({id, bookTitle, ImgURL, bookDescription, bookAuthor, bookPrice, bookTypes, bookYear, bookRating, bookPages})
})
const responseData = await response.json()
dispatch({
type: EDIT_BOOKS,
payload: responseData
})
}
}
EditScreen.js
import React, {useState, useEffect} from 'react'
import { StyleSheet, Text, View, ScrollView, TextInput, Button, KeyboardAvoidingView, Alert,
ActivityIndicator } from 'react-native'
import { useDispatch, useSelector } from 'react-redux'
import * as bookAction from '../redux/actions/bookAction'
import { Formik } from 'formik'
import * as Yup from 'yup'
import DropDownPicker from 'react-native-dropdown-picker'
const formSchema = Yup.object({
bookTitle: Yup.string().required('*required').min(5, '*must be between 5 to 50 characters').max(50, '*must be between 5 to 50 characters'),
ImgURL: Yup.string().required('*required'),
bookDescription: Yup.string().required('*required').min(30, '*must be at least 30 characters'),
bookAuthor: Yup.string().required('*required'),
bookPrice: Yup.number().required('*required'),
bookTypes: Yup.string().required('*required'),
bookYear: Yup.number().required('*required'),
bookRating: Yup.number().required('*required'),
bookPages: Yup.number().required('*required')
})
const AddBookScreen = props => {
const {id} = props.route.params
const book = useSelector(state => state.book.books.find(book => book._id === id))
const [isLoading, setIsLoading] = useState(false)
if(isLoading) {
return (
<View style={styles.centered}>
<ActivityIndicator size="large" />
</View>
)
}
const dispatch = useDispatch()
return (
<KeyboardAvoidingView
behavior="padding"
keyboardVerticalOffset={100}
style={{flex: 1}}>
<ScrollView>
<Formik
initialValues={{
id: id,
bookTitle: book.bookTitle,
ImgURL: book.ImgURL,
bookDescription: book.bookDescription,
bookAuthor: book.bookAuthor,
bookPrice: book.bookPrice.toString(),
bookTypes: book.bookTypes,
bookYear: book.bookYear.toString(),
bookRating: book.bookRating.toString(),
bookPages: book.bookPages.toString()
}}
validationSchema={formSchema}
onSubmit={(values) => {
console.log(values)
setIsLoading(true)
dispatch(bookAction.editBook(values))
.then(() => {
setIsLoading(false)
Alert.alert('book edited successfrully')
})
.catch(() => {
setIsLoading(false)
Alert.alert('an error occurred, please try again!')
})
}}>
{(props) =>
(
<View style={styles.form}>
<View style={styles.formGroup}>
<Text style={styles.label}>Book Title</Text>
<TextInput
style={styles.input}
onChangeText={props.handleChange('bookTitle')}
onBlur={props.handleBlur('bookTitle')}
value={props.values.bookTitle}
/>
<Text style={styles.error}>{props.touched.bookTitle && props.errors.bookTitle}</Text>
</View>
<View style={styles.formGroup}>
<Text style={styles.label}>Imgae URL</Text>
<TextInput
style={styles.input}
onChangeText={props.handleChange('ImgURL')}
onBlur={props.handleBlur('ImgURL')}
value={props.values.ImgURL}
/>
<Text style={styles.error}>{props.touched.ImgURL && props.errors.ImgURL}</Text>
</View>
<View style={styles.formGroup}>
<Text style={styles.label}>Book Description</Text>
<TextInput
style={styles.input}
onChangeText={props.handleChange('bookDescription')}
onBlur={props.handleBlur('bookDescription')}
value={props.values.bookDescription}
/>
<Text style={styles.error}>{props.touched.bookDescription && props.errors.bookDescription}</Text>
</View>
<View style={styles.formGroup}>
<Text style={styles.label}>Book Author</Text>
<TextInput
style={styles.input}
onChangeText={props.handleChange('bookAuthor')}
onBlur={props.handleBlur('bookAuthor')}
value={props.values.bookAuthor}
/>
<Text style={styles.error}>{props.touched.bookAuthor && props.errors.bookAuthor}</Text>
</View>
<View style={styles.formGroup}>
<Text style={styles.label}>Book Price</Text>
<TextInput
style={styles.input}
onChangeText={props.handleChange('bookPrice')}
onBlur={props.handleBlur('bookPrice')}
value={props.values.bookPrice}
keyboardType='numeric'
/>
<Text style={styles.error}>{props.touched.bookPrice && props.errors.bookPrice}</Text>
</View>
<View style={styles.formGroup}>
<Text style={styles.label}>Book Types</Text>
<TextInput
style={styles.input}
onChangeText={props.handleChange('bookTypes')}
onBlur={props.handleBlur('bookTypes')}
value={props.values.bookTypes}
/>
<Text style={styles.error}>{props.touched.bookTypes && props.errors.bookTypes}</Text>
</View>
<View style={styles.formGroup}>
<Text style={styles.label}>Book Year</Text>
<TextInput
style={styles.input}
onChangeText={props.handleChange('bookYear')}
onBlur={props.handleBlur('bookYear')}
value={props.values.bookYear}
keyboardType='numeric'
/>
<Text style={styles.error}>{props.touched.bookYear && props.errors.bookYear}</Text>
</View>
<View style={styles.formGroup}>
<Text style={styles.label}>Book Rating</Text>
<TextInput
style={styles.input}
onChangeText={props.handleChange('bookRating')}
onBlur={props.handleBlur('bookRating')}
value={props.values.bookRating}
keyboardType='numeric'
/>
<Text style={styles.error}>{props.touched.bookRating && props.errors.bookRating}</Text>
</View>
<View style={styles.formGroup}>
<Text style={styles.label}>Book Pages</Text>
<TextInput
style={styles.input}
onChangeText={props.handleChange('bookPages')}
onBlur={props.handleBlur('bookPages')}
value={props.values.bookPages}
keyboardType='numeric'
/>
<Text style={styles.error}>{props.touched.bookPages && props.errors.bookPages}</Text>
</View>
<View style={styles.buttonContainer}>
<Button title='save edit' onPress={props.handleSubmit} color='steelblue' />
</View>
</View>
)}
</Formik>
</ScrollView>
</KeyboardAvoidingView>
)
}
export default AddBookScreen
const styles = StyleSheet.create({
form: {
backgroundColor: "#ffffff",
padding: 20,
borderRadius: 10,
},
formGroup: {
width: "100%",
},
label: {
marginVertical: 10,
},
input: {
paddingHorizontal: 2,
paddingVertical: 8,
borderBottomColor: "#ccc",
borderBottomWidth: 1,
},
buttonContainer: {
marginTop: 20,
},
error: {
color: 'red'
},
centered: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
}
})
React Native Debugger
Edited
Manage to fix the error in debugger, by modifying the ReduxReducer.js
case EDIT_BOOKS:
return {
...state,
books: state.books.map(book => action.payload.find(item => item.id === book._id) || book)
}
The error is gone, but it didn't update the data
So I found the solution of my issue by Editing the ReduxReducer.js to
case EDIT_BOOKS:
return {
...state,
books: [...state.books]
}

My flatlist not rendering without remote debugger

I am working on an app by react native.i have a problem. when my app has a debug mode work very well. but in without debug mode, my Flatlist is not rendering all items.
I'm using redux and redux-saga
class ComponentGroups extends React.Component {
constructor (props){
super(props)
this.state = {
listBanner: this.props.item.listBanner
}
}
eventOnscroll = () => {
console.log('load list banner pls what wrong with u')
}
checkRenderView = () => {
if(this.state.listBanner.length && this.state.listBanner.length === 1) {
return (
<ComponentBanner item={this.state.listBanner[0]} typeTab={1}/>
)
}
const dimensions = Dimensions.get('window');
const screenWidth = dimensions.width;
return (
<ScrollView onScroll={this.eventOnscroll}>
<FlatList
style={{
flex: 1,
width: screenWidth
}}
showsHorizontalScrollIndicator={false}
horizontal={true}
data={this.state.listBanner}
removeClippedSubviews={false}
keyExtractor={(item, index) => index.toString()}
renderItem={({item, index}) => <ComponentBanner item={item} index={index} typeTab={1}/>}
/>
</ScrollView>
)
}
render () {
return (
<View style={styles.bannerItem}>
{this.state.listBanner.length && this.checkRenderView()}
</View>
)
}
}
and my component item:
class ComponentBanner extends React.Component {
constructor (props) {
super(props)
}
handlePressDetailBanner = () => {
detailImageScreen({item: this.props.item, typeTab: 1})
}
showViewNewsViewed = () => {
if(this.props.item.hot && this.props.item.watched) {
return (
<View style={styles.viewsLiked}>
<View style={styles.iconLike}>
<Text style={styles.news}>Mới</Text>
</View>
<View style={styles.iconView}>
<Text style={styles.viewed}>Đã xem</Text>
</View>
</View>
)
}
if(this.props.item.hot && !this.props.item.watched) {
return (
<View style={styles.viewsLiked}>
<View style={styles.iconLike}>
<Text style={styles.news}>Mới</Text>
</View>
</View>
)
}
if(!this.props.item.hot && this.props.item.watched) {
return (
<View style={styles.viewsLiked}>
<View style={styles.iconView}>
<Text style={styles.viewed}>Đã xem</Text>
</View>
</View>
)
}
}
render () {
return (
<View style={styles.bannerItem}>
<TouchableOpacity onPress ={this.handlePressDetailBanner}>
<Image source={{uri: this.props.item.url}} style={styles.imageItem} borderRadius={5} resizeMode='stretch'/>
</TouchableOpacity>
<View style={styles.titleLikes}>
{this.showViewNewsViewed()}
<Text numberOfLines={2} ellipsizeMode='tail' style={styles.textTitle}>{this.props.item.name}</Text>
<View style={styles.viewsLiked}>
<View style={styles.iconLike}>
<Icon
name='thumb-up'
color={this.props.item.userLike? Colors.mpAdvertise : Colors.cloud}
size={Fonts.size.regular}
/>
<Text style={styles.accountLike}>{this.props.item.totalLike}</Text>
</View>
<View style={styles.iconView}>
<Icon
name='eye'
color={Colors.cloud}
size={Fonts.size.regular}
/>
<Text style={styles.accountLike}>{this.props.item.totalView}</Text>
</View>
</View>
</View>
</View>
)
}
}
export default ComponentBanner
and Render Item by :
render () {
return (
<TouchableOpacity onPress ={this.handlePressDetailBanner} style={styles.detailImage}>
<View style={styles.bannerItem}>
<View>
<Image source={{uri: this.props.item.url ? this.props.item.url : ''}} style={styles.imageItem} borderRadius={5} resizeMode='stretch'/>
</View>
<View style={styles.titleLikes}>
<Text numberOfLines={2} ellipsizeMode='tail' style={styles.textTitle}>{this.props.item.name ? this.props.item.name : ''}</Text>
<View style={styles.viewsLiked}>
<View style={styles.iconLike}>
<Icon
name='thumb-up'
color={this.props.item.userLike? Colors.mpAdvertise : Colors.cloud}
size={Fonts.size.regular}
/>
<Text style={styles.accountLike}>{this.props.item.totalLike ? this.props.item.totalLike : ''}</Text>
</View>
<View style={styles.iconView}>
<Icon
name='eye'
color={Colors.cloud}
size={Fonts.size.regular}
/>
<Text style={styles.accountLike}>{this.props.item.totalView ? this.props.item.totalView : ''}</Text>
</View>
</View>
</View>
</View>
</TouchableOpacity>
)
}
}
UPDATE: sorry for forgot main screen rendering ComponentGroups:
constructor (props) {
super(props)
this.state = {
params: {
SourceType: '',
GroupCode: '',
NumOfRec: 10,
PageNum: 1,
TypeId: '',
},
data:[],
groupBanner: '',
groupVideo: '',
mainBanner: '',
listBanners: [],
listVideos: [],
isSelected: 1,
fetching: false,
onEndReachedCalledDuringMomentum: true
}
Navigation.events().bindComponent(this)
this.props.getData()
}
static getDerivedStateFromProps(nextProps, prevState){
if (nextProps.fetching !== prevState.fetching){
return {
fetching: nextProps.fetching,
data: nextProps.data,
listBanners: nextProps.listBanners,
listVideos: nextProps.listVideos
}
}
else return null;
}
componentDidUpdate(prevProps, prevState) {
if (prevState.fetching !== this.state.fetching) {
}
}
componentDidAppear () {
Navigation.mergeOptions(this.props.componentId, {
sideMenu: {
left: {
enabled: true
}
}
})
}
showSideMenu () {
Navigation.mergeOptions(this.props.componentId, {
sideMenu: {
left: {
visible: true
}
}
})
}
navigationButtonPressed ({ buttonId }) {
this.showSideMenu()
}
handlePressScroll = () => {
const {params} = this.state
if(!this.state.fetching) {
this.setState({
params: {
...params,
PageNum: params.PageNum + 1,
}
}, () => {
const data = Object.keys(this.state.params).map(key => key + '=' + this.state.params[key]).join('&')
console.log(data)
if(this.state.isSelected === 2) {
this.props.getDataBanner(data)
}
if(this.state.isSelected === 3) {
this.props.getDataVideo(data)
}
})
} else {
return null
}
}
handlePressSwitchTab = (value) => {
this.setState({
isSelected: value,
params: {
SourceType: 1,
GroupCode: '',
NumOfRec: 10,
PageNum: 1,
TypeId: 0
},
listBanners: [],
listVideos: [],
},()=>{
if(value === 1) {
this.props.resetStateHome()
this.props.getData()
}
if(value === 2) {
this.props.resetStateHome()
const data = Object.keys(this.state.params).map(key => key + '=' + this.state.params[key]).join('&')
this.props.getDataBanner(data)
}
if(value === 3) {
this.props.resetStateHome()
const data = Object.keys(this.state.params).map(key => key + '=' + this.state.params[key]).join('&')
this.props.getDataVideo(data)
}
})
}
formatData = (data, numColumns) => {
const numberOfFullRows = Math.floor(data.length / numColumns)
let numberOfElementsLastRow = data.length - (numberOfFullRows * numColumns)
while (numberOfElementsLastRow !== numColumns && numberOfElementsLastRow !== 0) {
data.push({ key: `blank-${numberOfElementsLastRow}`, empty: true })
numberOfElementsLastRow++
}
return data
}
handlePressBanner = () => {
detailImageScreen({item: this.state.data.mainBanner})
}
eventScroll = () => {
console.log(45544)
}
renderViewByState = () => {
const numColumns = 2;
if(this.state.data && this.state.data.mainBanner && this.state.data.mainBanner.url) {
var url = this.state.data.mainBanner.url
}
if (this.state.isSelected === 1) {
if(this.state.fetching) {
return (
<View style={styles.styleLoadMore}>
<BallIndicator color={Colors.mpAdvertise} animationDuration={800} />
</View>
)
} else {
return (
<ScrollView onScroll={this.eventScroll} >
<View style={styles.centered}>
<TouchableOpacity onPress={this.handlePressBanner} style={styles.mainBannerImage}>
<Image source={{uri: url}} style={styles.logo} borderRadius={5} resizeMode='stretch'/>
</TouchableOpacity>
</View>
<View style={styles.containerContent}>
<View style={styles.sectionBanner} >
<View>
<Text style={styles.textBanner}>BANNERS</Text>
</View>
<View style={styles.listBanner}>
<FlatList
data={this.state.data.groupBanner}
keyExtractor={(item, index) => index.toString()}
renderItem={({item, index}) => <ComponentGroups item={item} index={index} />}
/>
</View>
</View>
<View style={styles.sectionBanner} >
<View>
<Text style={styles.textBanner}>VIDEOS</Text>
</View>
<View style={styles.listBanner}>
<ScrollView>
<FlatList
data={this.state.data.groupVideo}
keyExtractor={(item, index) => index.toString()}
renderItem={({item, index}) => <ComponentGroupsVideo item={item} index={index} />}
/>
</ScrollView>
</View>
</View>
</View>
</ScrollView>
)
}
}
if (this.state.isSelected === 2) {
if(this.state.fetching && this.state.params.PageNum < 2) {
return (
<View style={styles.styleLoadMore}>
<BallIndicator color={Colors.mpAdvertise} animationDuration={800} />
</View>
)
}
return (
<ScrollView onScroll={this.eventScroll}>
<View style={styles.listBanner}>
<FlatList
extraData={this.state}
ListFooterComponent = {this.renderFooter}
data={this.state.listBanners}
keyExtractor={(item, index) => index.toString()}
renderItem={({item, index}) => <BannerItem item={item} index={index}/>}
numColumns={countNumber}
onEndReached={this.handlePressScroll}
onEndReachedThreshold={0.05}
/>
</View>
</ScrollView>
)
}
if (this.state.isSelected === 3) {
if(this.state.fetching && this.state.params.PageNum < 2) {
return (
<View style={styles.styleLoadMore}>
<BallIndicator color={Colors.mpAdvertise} animationDuration={800} />
</View>
)
}
return (
<View style={styles.listBanner}>
<FlatList
extraData={this.state}
data={this.state.listVideos}
ListFooterComponent = {this.renderFooter}
keyExtractor={(item, index) => index.toString()}
renderItem={({item, index}) => <VideosItem item={item} index={index} typeTab={3}/>}
numColumns={countNumber}
onEndReached={this.handlePressScroll}
onEndReachedThreshold={0.01}
/>
</View>
)
}
}
renderFooter = () => {
if(this.state.fetching && this.props.isStopScroll && this.state.params.PageNum > 1) {
return (
<View style={styles.styleLoadMoreFooter}>
<BallIndicator color={Colors.mpAdvertise} animationDuration={800} />
</View>
)
}
return null
}
render () {
return (
<View style={styles.mainContainer} testID='launchScreen'>
<View style={styles.tabBarHome}>
<View style={[styles.itemTab, this.state.isSelected === 1 ? styles.itemTabActive : null]}>
<TouchableOpacity testID='loginScreenLoginButton' style={styles.loginButtonWrapper} onPress={() => this.handlePressSwitchTab(1)}>
<View style={styles.loginButton}>
<Text style={[styles.itemText, this.state.isSelected === 1 ? styles.itemTextActive : null]}>Nổi bật</Text>
</View>
</TouchableOpacity>
</View>
<View style={[styles.itemTab, this.state.isSelected === 2 ? styles.itemTabActive : null]}>
<TouchableOpacity testID='loginScreenLoginButton' style={styles.loginButtonWrapper} onPress={() => this.handlePressSwitchTab(2)}>
<View style={styles.loginButton}>
<Text style={[styles.itemText, this.state.isSelected === 2 ? styles.itemTextActive : null]}>Banner</Text>
</View>
</TouchableOpacity>
</View>
<View style={[styles.itemTab, this.state.isSelected === 3 ? styles.itemTabActive : null]}>
<TouchableOpacity testID='loginScreenLoginButton' style={styles.loginButtonWrapper} onPress={() => this.handlePressSwitchTab(3)}>
<View style={styles.loginButton}>
<Text style={[styles.itemText, this.state.isSelected === 3 ? styles.itemTextActive : null]}>Videos</Text>
</View>
</TouchableOpacity>
</View>
</View>
{this.renderViewByState()}
</View>
)
}
}
const mapStateToProps = (state) => {
return {
data: state.home.data,
listBanners: state.home.listBanners,
listVideos: state.home.listVideos,
fetching: state.home.fetching,
cart: state.login.dataCart,
isStopScroll: state.home.isStopScroll
}
}
const mapDispatchToProps = (dispatch) => {
return {
getData: () => dispatch(HomeActions.homeRequest()),
getDataBanner: (data) => dispatch(HomeActions.bannerRequest(data)),
getDataVideo: (data) => dispatch(HomeActions.videoRequest(data)),
resetStateHome: () => dispatch(HomeActions.resetStateHome())
}
}
export default connect(mapStateToProps, mapDispatchToProps)(HomeScreen)
The above code, render my item in flatlist .in debug mode working very good, but in release mode, it's not rendering at all
please try this
<FlatList
style={{
flex: 1,
width: screenWidth
}}
extraData={this.state} //<--- add this line
showsHorizontalScrollIndicator={false}
horizontal={true}
data={this.state.listBanner}
removeClippedSubviews={false}
keyExtractor={(item, index) => index.toString()}
renderItem={({item, index}) => <ComponentBanner item={item} index={index} typeTab={1}/>}
/>

React Native FlatList Touchable Opacity

I used FlatList to display the title that is in the data. I added in TouchableOpacity for the FlatList. So for example, when I click 'First', I want to show the the data from 'mood' and it will show the list of passionate,rousing and confident. I want to show the list on a new file/screen and show purely the mood of list.
This is my data:
const Mock =
[
{ title: 'First',
mood:
[
{name: 'passionate'},
{name: 'rousing'},
{name: 'confident'},
],
},
{ title: 'Second',
mood:
[
{name: 'rollicking'},
{name: 'cheerful'},
{name: 'fun'},
{name: 'sweet'},
{name: 'amiable'},
{name: 'natured'}
],
This is my FlatList code:
export default class Cluster1 extends Component{
render() {
return (
<View>
<FlatList
data={Mock}
renderItem={({ item, index }) => {
return <FlatListItem item={item} index={index} />;
}}
/>
</View>
);
}
}
class FlatListItem extends Component {
render() {
return (
<View style={styles.list}>
<View>
<TouchableOpacity>
<Text style={styles.itemTitle}>{this.props.item.title}</Text>
</TouchableOpacity>
</View>
</View>
);
}
}
What should I do with the TouchableOpacity when I want to show the mood name when I click the title?
This is my style code
const styles = StyleSheet.create({
itemTitle:{
fontSize: 25,
fontWeight: 'bold',
color: 'white',
margin: 20,
},
},
list:{
flex: 1,
backgroundColor: '#00BCD4',
alignItems: 'center',
justifyContent: 'space-between',
flexDirection: 'row',
},
});
You should modify your code as below to do this:
export default class Cluster1 extends Component {
render() {
return (
<View style={{ margin: 30, backgroundColor: '#ddd' }}>
<FlatList
data={Mock}
renderItem={({ item, index }) => {
return <FlatListItem item={item} index={index} />;
}}
/>
</View>
);
}
}
class FlatListItem extends Component {
state = { showItemIndex: [false, false] };
_onPress = index => () => {
let showItemIndex = this.state.showItemIndex;
showItemIndex[index] = !this.state.showItemIndex[index];
this.setState({ showItemIndex });
};
render() {
return (
<View style={styles.list}>
<View>
<TouchableOpacity onPress={this._onPress(this.props.index)}>
<Text style={styles.itemTitle}>{this.props.item.title}</Text>
</TouchableOpacity>
{this.state.showItemIndex[this.props.index] && (
<FlatList
data={this.props.item.mood}
extraData={this.state.showItemIndex}
renderItem={({ item, index }) => {
return (
<Text item={item} index={index}>
{item.name}
</Text>
);
}}
/>
)}
</View>
</View>
);
}
}
Use this, it's should be work fine for you:
Link: https://github.com/oblador/react-native-collapsible
Link: https://github.com/naoufal/react-native-accordion
Link: https://github.com/cuiyueshuai/react-native-expandable-section-flatlist
You could set a state variable which gets updated whenever your TouchableOpacity gets pressed. And then you conditionally render the title or the list of mood names:
class FlatListItem extends Component {
constructor(props) {
super(props);
this.state = {collapsed: true}
}
render() {
return (
<View style={styles.list}>
<View>
<TouchableOpacity
onPress={this.onPress}
>
<Text style={styles.itemTitle}>
{this.props.item.title}
</Text>
</TouchableOpacity>
{this.state.collapsed ?
<View /> :
<View>
{this.props.item.mood.map(mood => <Text>{mood.name}</Text>)}
</View>}
</View>
</View>
);
}
onPress = () => {
this.setState({collapsed: !this.state.collapsed})
}
}
You can do this by separating out the datasource that is required to show in the list.
First to display title: you can do something like this
export default class Cluster1 extends Component {
state = {
data: []
};
componentWillMount() {
const titles = Mock.forEach(data => data.title);
this.setState({
data: titles
});
}
onItemClick = item => {
const itemIndex = Mock.findIndex(data => (
data.title === item
));
const values = Mock[itemIndex].mood;
this.setState({
data: values
});
};
render() {
return (
<View>
<FlatList
data={this.state.data}
renderItem={({ item, index }) => {
return (
<FlatListItem
item={item}
index={index}
onItemClick={this.itemClick}
/>
);
}}
/>
</View>
);
}
}
and then in your FlatlistItem code:
class FlatListItem extends Component {
render() {
return (
<View style={styles.list}>
<View>
<TouchableOpacity onPress={() =>
this.props.onItemClick(this.props.item)
}>
<Text style={styles.itemTitle}>
{
this.props.item.name ?
this.props.item.name : this.props.item
}
</Text>
</TouchableOpacity>
</View>
</View>
);
}
}

Tabbed navigation between screens in react native

I use a StackNavigator based on the react-native-elements example and I want to enable something similar to a link with an ID as a parameter. I want to link to this screen:
const FontsTab = StackNavigator({
Home: {
screen: FontsTabView,
path: '/',
navigationOptions: ({ navigation }) => ({
title: 'Fonts',
headerLeft: (
<Icon
name="menu"
size={30}
type="entypo"
style={{ paddingLeft: 10 }}
onPress={() => navigation.navigate('DrawerOpen')}
/>
),
}),
},
Detail: {
screen: FontsDetailTabView,
path: 'fonts_detail',
navigationOptions: {
title: 'Fonts Detail',
},
},
});
I have this screen where I want the click of the text of an item to open the FontsTabView with the ID as a parameter. I would like to achieve something like the following:
<Text onPress={ (navigation)=> navigation.navigate('FontsTabView ', { id: {item.id} }) } style={styles.listHeader} >{item.title}</Text>
How can it be done?
class Icons extends Component {
constructor() {
super();
const ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2,
});
this.state = {
selectedIndex: 0,
value: 0.5,
dataSource: ds.cloneWithRows(list1),
isLoading: true
};
this.updateIndex = this.updateIndex.bind(this);
this.renderRow = this.renderRow.bind(this);
}
updateIndex(selectedIndex) {
this.setState({ selectedIndex });
}
renderRow(rowData, sectionID) {
return (
<ListItem
key={sectionID}
onPress={log}
title={rowData.title}
icon={{ name: rowData.icon }}
/>
);
}
_renderList = ({ item, navigation }) => {
return (
<TouchableWithoutFeedback onPress={(event) => this._selectedItem(item.key)}>
<View style={styles.listRowContainer}>
<View style={styles.listinside1Container}>
<Image style={styles.listImage} source={item.icon} />
<View style={styles.listContainer} onPress={(event) => this._selectedItem(item.text)} >
<Text onPress={ (navigation)=> navigation.navigate('DrawerOpen') } style={styles.listHeader} >{item.title}</Text>
<Text style={styles.listValue} >{item.value}</Text>
<Image
style={{width: 50, height: 50}}
source={{uri: item.img}}
/>
</View>
</View>
</View>
</TouchableWithoutFeedback>
);
}
componentDidMount(){
return fetch('https://www.koolbusiness.com/in.json')
.then((response) => response.json())
.then((responseJson) => {
this.setState({
isLoading: false,
dataSource: responseJson.movies,
}, function(){
});
})
.catch((error) =>{
console.error(error);
});
}
render() {
if(this.state.isLoading){
return(
<View style={{flex: 1, padding: 20}}>
<ActivityIndicator/>
</View>
)
}
const { navigation } = this.props;
const buttons = ['Button1', 'Button2'];
const { selectedIndex } = this.state;
if(this.state.isLoading){
return(
<View style={{flex: 1, padding: 20}}>
<ActivityIndicator/>
</View>
)
}
return (
<ScrollView>
<View style={styles.headerContainer}>
<Icon color="white" name="invert-colors" size={62} />
<Text style={styles.heading}>Trending Ads India</Text>
</View>
<View style={styles.MainContainer}>
</View>
<View style={styles.mainWrapper} >
<FlatList data={this.state.dataSource} renderItem={this._renderList} keyExtractor={(item, index) => index.toString()} />
</View>
</ScrollView>
);
}
}
You were close but not quite there,
Try to render your item like below;
<Text onPress={ ()=> this.props.navigation.navigate('FontsTabView', { id: item.id }) } style={styles.listHeader} >
{item.title}
</Text>
Then in FontsTabView you can read the parameter like below and render your screen accordingly.
this.props.navigation.state.params.id