react native textinput takes the value from another textinput - react-native

when I enter the number of items in the textInput Item number, i hope the price of the item automatically appears in the textIinput Price.
here is my quick script that won't work
please help me to improve, thank you
constructor(props){
super(props);
this.state={
qty: 0,
prc: 0,
}
}
price = () =>{
var price = this.state.qty * 250;
this.setState({
prc: price,
})
}
render(){
return (
<View style={styles.container}>
.....
<View style={{...}}>
<Text style={{...}}>Item number :</Text>
<TextInput
...
onChangeText={(entry) => {
this.setState({qty: entry})
this.price();
}}
...
/>
</View>
<View style={{...}}>
<Text style={{...}}>Price :</Text>
<TextInput
....
value={this.state.prc}
onChangeText={(entry) => {
this.setState({prc: entry})
}}
....
/>
</View>
</View>

Modify your code like below
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<Text>Item number :</Text>
<TextInput
value={this.state.qty}
onChangeText={(entry) => {
this.setState({
qty: entry,
prc: entry * 250
})
}}
/>
<Text>Price :</Text>
<TextInput
value={this.state.prc}
onChangeText={(entry) => {
this.setState({
prc: entry
})
}}
/>
</View>
If you really want to use function price provide it as a callback function.
<TextInput
value={this.state.qty}
onChangeText={(entry) => {
this.setState({
qty: entry
}, () => this.price())
}}
/>
Edit - I have added a sample application check this
import React, { Component } from "react";
import { View, Text, TextInput, Button, StyleSheet } from "react-native";
export default class Example extends Component {
state = {
qty: 0,
prc: 0,
}
price = () => {
let price = this.state.qty * 250;
this.setState({
prc: price,
})
}
render() {
return (
<View style={styles.container}>
<Text>Item number :</Text>
<TextInput
value={this.state.qty}
onChangeText={(entry) => {
this.setState({
qty: entry
}, () => this.price())
}}
/>
<Text>Price :</Text>
<TextInput
value={this.state.prc}
onChangeText={(entry) => {
this.setState({
prc: entry
})
}}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center"
}
});
Hope this helps you. Feel free for doubts.

import React, { Component, useState } from 'react';
import { View, Text, TextInput } from 'react-native';
export default Example = () => {
const [price, setprice] = useState(0);
return (
<View>
<Text>Item number :</Text>
<TextInput
onChangeText={(entry) => setprice(entry)}
/>
<Text>Price :</Text>
<TextInput defaultValue={(parseInt(price) * 250).toString()}></TextInput>
</View>
);
}

Related

react native recyclerlistview if state changed its scroll to top

If I change any state or change the textinput value, if it changed then its scroll to top and leave the textinput. Or If I like one product, its autoamticlly scroll to top if the array is changed. Anyone can help me ?
import React, { useState, useMemo, useEffect, forwardRef, memo } from 'react';
import { StyleSheet, Text, TextInput, View, TouchableOpacity, Image, Dimensions } from 'react-native';
import { TouchableWithoutFeedback } from 'react-native-gesture-handler';
import { RecyclerListView, LayoutProvider, DataProvider } from 'recyclerlistview';
import { useSelector, useDispatch } from 'react-redux';
import { AntDesign } from '#expo/vector-icons';
import { addAmountOnCartItem } from '../../../redux/slice/product/shopping_cart';
import faker from 'faker';
import ButtonWithoutLoader from '../../ButtonWithoutLoader';
const { width, height } = Dimensions.get('window');
const Shopping_cart_list = ({ datas, onPressSetting }) => {
const dispatch = useDispatch();
const provider = useMemo(() => {
return new DataProvider(
(r1, r2) => {
return r1 !== r2;
},
index => {
return 'id:' + index;
}
)
}, []);
const dataProvider = useMemo(() => {
return provider.cloneWithRows(datas);
}, [datas, provider]);
const [update, updateRecycler] = useState({
update: false
});
const handleChange = (e, product_id) => {
dispatch(addAmountOnCartItem({ value: e, product_id }));
updateRecycler(prevState => {
return {
update: !prevState
}
});
};
const layoutProvider = new LayoutProvider((i) => {
return dataProvider.getDataForIndex(i).type;
}, (type, dim) => {
switch(type) {
case 'NORMAL':
dim.height = 250;
dim.width = width * 0.9;
break;
default:
dim.height = 0;
dim.width = 0;
break;
}
});
const RenderData = memo(({ product_id, product_name, price, product_image, amount, username }) => {
return (
<TouchableWithoutFeedback style={{height: 250, backgroundColor: '#fff', marginBottom: 16}}>
<View style={styles.header}>
<TouchableOpacity style={styles.profile_info}>
<Image source={{uri: faker.image.avatar()}} resizeMode="contain" style={styles.profile_image} />
<Text style={styles.username}>{ username }</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => onPressSetting(product_id)}>
<AntDesign name="setting" size={24} color="#444" />
</TouchableOpacity>
</View>
<View style={styles.mainContainer}>
<Image source={{uri: product_image}} style={styles.image} />
<View>
<Text style={styles.text}>{product_name}</Text>
<Text style={styles.text}>Preis: {price}</Text>
<View style={{flexDirection: 'row', alignItems: 'center'}}>
<Text style={styles.text}>Anzahl: </Text>
<AntDesign name="minussquareo" style={{marginRight: 6}} size={24} color="black" />
<TextInput onBlur={() => handleChange(1, product_id)} value={ isNaN(amount) ? '' : amount.toString() } onChangeText={e => handleChange(e, product_id)} style={{height: 28, width: 28, borderRadius: 4, textAlign: 'center', backgroundColor: '#eee'}} />
<AntDesign name="plussquareo" style={{marginLeft: 6}} size={24} color="black" />
</View>
</View>
</View>
<View style={[styles.header, { marginTop: 4 }]}>
<ButtonWithoutLoader onPress={() => updateRecycler(prevState => !prevState)} title="Jetzt Kaufen!" width={width * 0.9} />
</View>
</TouchableWithoutFeedback>
)
});
const rowRenderer = (type, data) => {
const { product_id, product_name, price, product_image, amount, username } = data.item;
return <RenderData product_id={product_id} product_name={product_name} price={price} product_image={product_image} amount={amount} username={username} />
};
return (
<View style={{flex: 1, paddingBottom: 85}}>
<RecyclerListView
dataProvider={dataProvider}
layoutProvider={layoutProvider}
forceNonDeterministicRendering={true}
rowRenderer={rowRenderer}
style={{marginLeft: width * 0.05, marginRight: width * 0.05}}
extendedState={update}
scrollViewProps={{showsVerticalScrollIndicator: false}}
/>
</View>
)
};
Flatlist:
import React, { useState, useRef, memo, useMemo } from 'react';
import { StyleSheet, Animated, FlatList, Text, TextInput, View, TouchableOpacity, Image, Dimensions } from 'react-native';
import { TouchableWithoutFeedback } from 'react-native-gesture-handler';
import { useSelector, useDispatch } from 'react-redux';
import { useNavigation } from '#react-navigation/core';
import { AntDesign } from '#expo/vector-icons';
import { addAmountOnCartItem } from '../../../redux/slice/product/shopping_cart';
import faker from 'faker';
import ButtonWithoutLoader from '../../ButtonWithoutLoader';
const { width } = Dimensions.get('window');
const Shopping_cart = ({ datas, onPressSetting }) => {
const dispatch = useDispatch();
const navigation = useNavigation();
const [update, updateRecycler] = useState({
update: false
});
const handleChange = (e, product_id) => {
dispatch(addAmountOnCartItem({ value: e, product_id }));
updateRecycler(prevState => {
return {
update: !prevState
}
});
};
const RenderItem = memo(({ item }) => {
const { product_id, product_name, price, product_image, amount, username, size, colors, desc, selectedColor, selectedSize } = item.item;
return (
<TouchableWithoutFeedback style={{height: 300, backgroundColor: '#fff', marginBottom: 16}}>
<View style={styles.header}>
<TouchableOpacity style={styles.profile_info}>
<Image source={{uri: faker.image.avatar()}} resizeMode="contain" style={styles.profile_image} />
<Text style={styles.username}>{ username }</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => onPressSetting(product_id)}>
<AntDesign name="setting" size={24} color="#444" />
</TouchableOpacity>
</View>
<TouchableOpacity onPress={() => navigation.navigate('ProductStack', {
product_id,
desc,
price,
product_image,
username,
user_profil_image: faker.image.avatar(),
size,
colors,
})} style={styles.mainContainer}>
<Image source={{uri: product_image}} style={styles.image} />
<View>
<Text style={styles.text}>{product_name}</Text>
<Text style={styles.text}>Preis: {price}</Text>
<Text style={styles.text}>Farbe: {selectedColor}</Text>
<Text style={styles.text}>Größe: {selectedSize}</Text>
<View style={{flexDirection: 'row', alignItems: 'center'}}>
<Text style={styles.text}>Anzahl: </Text>
<AntDesign name="minussquareo" style={{marginRight: 6}} size={24} color="black" />
<TextInput onBlur={() => handleChange(1, product_id)} value={ isNaN(amount) ? '' : amount.toString() } onChangeText={e => handleChange(e, product_id)} style={{height: 28, width: 28, borderRadius: 4, textAlign: 'center', backgroundColor: '#eee'}} />
<AntDesign name="plussquareo" style={{marginLeft: 6}} size={24} color="black" />
</View>
</View>
</TouchableOpacity>
<View style={[styles.header, { marginTop: 4 }]}>
<ButtonWithoutLoader onPress={() => updateRecycler(prevState => !prevState)} title="Jetzt Kaufen!" width={width * 0.9} />
</View>
</TouchableWithoutFeedback>
);
});
return (
<FlatList
data={datas}
keyExtractor={item => item.item.product_id + Math.random(100)}
renderItem={({ item }) => <RenderItem item={item}/>}
contentContainerStyle={{justifyContent: 'center', alignItems: 'center'}}
removeClippedSubviews={true}
initialNumToRender={2}
maxToRenderPerBatch={1}
extraData={update}
updateCellsBatchingPeriod={100}
/>
);
};
........................................................................................................................................................................................
If you see the docs of RecyclerView, you will see for extendedState prop:
In some cases the data passed at row level may not contain all the info that the item depends upon, you can keep all other info outside and pass it down via this prop. Changing this object will cause everything to re-render. Make sure you don't change it often to ensure performance. Re-renders are heavy.
Based on your code you are updating this prop in button click and text change handler, this will eventually re-render your screen as per the description. So, here you would need to revise your logic to avoid unnecessary re-renders.
I recently just solved this problem in my own project so I'd be glad to help.
The main issue is that you can't have ANY UI related state in your list items. None. You must instead move all of that information to be transferred through props.
I see your only instance of that is through your TextInput. This could be difficult. So my first question would then be: do you really need RecyclerListView? Unless your list is thousands of items long, and items have a complicated/image intensive UI, I'd suggest FlatList for this project.
If you do continue to need RecyclerListView, I'd suggest making it so after a textinput is finished ('finished' as in maybe after a 3 second timer or, when they press 'submit' or something like that), you change the dataProvider's data object. Change the item at the index you need, and then resubmit it with cloneWithRows()

How can use useState() with Flatlist data?

I've had a problem when i used useState(). i have to filter by searched words on my data and list.
i need to define my data list with State (i'd list with searched words) but when i use State, i've taken 'Invalid Hook' error.
let [list, setList] = useState(data);
//called
data={list}
I don't find where i use that , I couldn't fix for 3 days, i can't reach next step :( I hope i'll fix with expert helps...
import React, {Component, useState} from 'react'
import {
Text,
StyleSheet,
View,
FlatList,
SafeAreaView,
ScrollView,
Image,
TextInput,
} from 'react-native'
import data from '../../data'
export default class Flatlistexample extends Component {
render () {
//defined below
let [list, setList] = useState(data);
seachFilter=(text)=>{
const newData = data.filter(item=>{
const listitem= `${item.name.toLowerCase()} ${item.company.toLowerCase()}`;
return listitem.indexOf(text.toLowerCase())
})
};
return (
<SafeAreaView
style={{
flex: 1,
}}>
<FlatList
//called
data={list}
renderItem={({item, index})=>{
return (
<ScrollView>
<SafeAreaView
style={[
styles.container,
{backgroundColor: index % 2 === 0 ? '#fafafa' : '#bbb'},
]}>
<Image style={styles.profile} source={{uri: item.picture}} />
<View style={styles.rightside}>
<Text style={styles.name}>{item.name}</Text>
<Text style={styles.company}>{item.company}</Text>
</View>
</SafeAreaView>
</ScrollView>
)
}}
keyExtractor={item => item._id}
ListHeaderComponent={() => {
const [search, setSearch] = useState('');
return (
<View style={styles.seachContainer}>
<TextInput
style={styles.textInput}
placeholder={'Search...'}
value={search}
onChangeText={text=>{
setSearch(text)
}}
></TextInput>
</View>
)
}}
/>
</SafeAreaView>
)
}
}
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
alignItems: 'center',
borderBottomWidth: 1,
borderColor: 'gray',
},
profile: {
width: 50,
height: 50,
borderRadius: 25,
marginLeft: 10,
},
rightside: {
marginLeft: 20,
justifyContent: 'space-between',
marginVertical: 5,
},
name: {
fontSize: 22,
marginBottom: 10,
},
searchContainer: {
padding: 10,
borderWidth: 2,
borderColor: 'gray',
},
textInput: {
fontSize: 16,
backgroundColor: '#f9f9f9',
padding: 10,
},
})
Thank you
React hooks can be used with functional component only, here you are using class component
You need to understand the difference between functional component and class component first.
Here you are using class component so your state should be manageed in the following way
export default class Flatlistexample extends Component {
constructor(props)
{
this.state={list:[]}
}
}
and to update list
this.setState({list: <array of data>})
If you want to use hooks, your component needs to be changed something like the following:
const Flatlistexample = () => {
//defined below
let [list, setList] = useState(data);
seachFilter = (text) => {
const newData = data.filter(item => {
const listitem = `${item.name.toLowerCase()} ${item.company.toLowerCase()}`;
return listitem.indexOf(text.toLowerCase())
})
};
return (
<SafeAreaView
style={{
flex: 1,
}}>
<FlatList data={list} renderItem={Your flatlist Item}/>
</SafeAreaView>
)
}
export default Flatlistexample
Here you go, I've added lots of comments. I hope you find this instructive. Let me know if you have questions!
import React, { useMemo, useState } from 'react'
import {
Text,
StyleSheet,
View,
FlatList,
SafeAreaView,
ScrollView,
Image,
TextInput,
} from 'react-native'
import data from '../../data'
// changed this to a functional component so you can use hooks. You can't use hooks in class components.
const Flatlistexample = () => {
// you don't actually need to `useState` for your list, since you're always just filtering `data`
// you would need to use `useState` if you were receiving data from an API request, but here it's static
const [search, setSearch] = useState('') // this should live in the main component so you can filter the list
const parsedSearch = search.toLowerCase() // do this once outside the filter, otherwise you're converting it for each item in the data array
const filteredList = useMemo(
() =>
data.filter(item => {
const itemText = `${item.name.toLowerCase()} ${item.company.toLowerCase()}`
return itemText.indexOf(parsedSearch) > -1 // returns `true` if search is found in string
}),
[parsedSearch], // this will only run if parsedSearch changes
)
return (
<SafeAreaView style={{ flex: 1 }}>
<FlatList
//called
data={filteredList} // use the filtered list here
renderItem={({ item, index }) => {
return (
<ScrollView>
<SafeAreaView
style={[
styles.container,
{ backgroundColor: index % 2 === 0 ? '#fafafa' : '#bbb' },
]}
>
<Image style={styles.profile} source={{ uri: item.picture }} />
<View style={styles.rightside}>
<Text style={styles.name}>{item.name}</Text>
<Text style={styles.company}>{item.company}</Text>
</View>
</SafeAreaView>
</ScrollView>
)
}}
keyExtractor={item => item._id}
ListHeaderComponent={() => {
return (
<View style={styles.seachContainer}>
<TextInput
style={styles.textInput}
placeholder={'Search...'}
value={search}
onChangeText={text => {
setSearch(text)
}}
/>
</View>
)
}}
/>
</SafeAreaView>
)
}
export default Flatlistexample

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]
}

Add a button "see more" in FlatList?

I use flatList to make a list of elements. I would like to show 15 elements and then add a button "see more" to show the next 15 etc.
I was about tu use this tutorial : https://aboutreact.com/react-native-flatlist-pagination-to-load-more-data-dynamically-infinite-list/
But I don't need to use fetch, I already have set up the data (state.listData) and in fact, I'm a little lost on how to adapt it...
I thought that maybe anyone could help me a little.
Thanks a lot
this.state = {
selectedId: '',
setSelectedId:'',
listData:''
}
};
renderItem = ({ item }) => {
const backgroundColor = item.id === this.selectedId ? "transparent" : "fff";
return (
<View style={{flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center'}}>
<Item
item={item}
onPress={() => this.props.navigation.navigate('UpdateTripsForm')}
style={{ backgroundColor }}
/>
<Image source={require("../../assets/images/arrow.png")} style={{width: 15, height:15, justifyContent: 'center'}}/>
</View>
);
};
initListData = async () => {
let list = await getFlights(0);
if (list) {
this.setState({
listData: list
});
}
};
render() {
return (
<SafeAreaView style={styles.container}>
<FlatList
data={this.state.listData}
renderItem={this.renderItem}
maxToRenderPerBatch={15}
keyExtractor={(item) => item.id}
extraData={this.selectedId}
/>
<TouchableOpacity
style={styles.touchable2}
onPress={() => this.props.navigation.goBack()}
>
<View style={styles.view2}>
<Text style={styles.textimg2}>
{i18n.t("tripsform.action.back")}
</Text>
</View>
<Image
source={require("../../assets/images/btn-background.png")}
style={styles.tripsimg2}
/>
</TouchableOpacity>
</SafeAreaView>
);
};
}
I just tried this thanks to #Pramod 's answer :
const Item = ({ item, onPress, style }) => (
<TouchableOpacity onPress={onPress} style={[styles.flightsListitem, style]}>
<Text style={styles.h4}>{item.id}</Text>
</TouchableOpacity>
);
export default class FlightsList extends Component {
constructor(props) {
super(props);
this.state = {
selectedId: '',
setSelectedId:'',
listData:'',
page:1,
perPage:2,
loadMoreVisible:true,
displayArray:[]
}
};
renderItem = ({ item }) => {
const backgroundColor = item.id === this.selectedId ? "transparent" : "fff";
return (
<View style={{flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center'}}>
<Item
item={item}
onPress={() => this.props.navigation.navigate('UpdateTripsForm')}
style={{ backgroundColor }}
/>
<Image source={require("../../assets/images/arrow.png")} style={{width: 15, height:15, justifyContent: 'center'}}/>
</View>
);
};
initListData = async () => {
let list = await getFlights(0);
if (list) {
this.setState({
listData: list
});
}
};
componentDidMount(){
this.setNewData()
// console.log(tempArray)
}
setNewData(){
var tempArray=[]
if(this.state.listData.length == this.state.displayArray.length){
this.setState({
loadMoreVisible:false
})
}else{
for(var i=0; i<(this.state.page*this.state.perPage); i++){
tempArray.push(this.state.listData)
}
this.setState({
displayArray: tempArray,
loadMoreVisible:true
})
}
}
loadMore(){
this.setState({
page: this.state.page+1
},()=>{
this.setNewData()
})
}
async UNSAFE_componentWillMount() {
this.initListData();
}
render() {
return (
<ImageBackground
source={require("../../assets/images/background.jpg")}
style={styles.backgroundImage}
>
<Header
backgroundImage={require("../../assets/images/bg-header.png")}
backgroundImageStyle={{
resizeMode: "stretch",
}}
centerComponent={{
text: i18n.t("mytrips.title"),
style: styles.headerComponentStyle,
}}
containerStyle={[styles.headerContainerStyle, { marginBottom: 0 }]}
statusBarProps={{ barStyle: "light-content" }}
/>
<SafeAreaView style={styles.container}>
<FlatList
data={this.state.displayArray}
renderItem={this.renderItem}
keyExtractor={(item) => item.id}
extraData={this.selectedId}
/>
{this.state.loadMoreVisible == true?
<Button style={{width:'100%', height:10, backgroundColor:'green', justifyContent:'center', alignItems:'center'}}
title = 'load more'
onPress={()=>{this.loadMore()}}>
</Button>:null}
<TouchableOpacity
style={styles.touchable2}
onPress={() => this.props.navigation.goBack()}
>
<View style={styles.view2}>
<Text style={styles.textimg2}>
{i18n.t("tripsform.action.back")}
</Text>
</View>
<Image
source={require("../../assets/images/btn-background.png")}
style={styles.tripsimg2}
/>
</TouchableOpacity>
</SafeAreaView>
</ImageBackground>
);
};
}
the flatlist is not displayed : I get :
You can user pagination method with per page limit so that you can have granular control
Load the array per page when component mount
On every click increase the per page and based on per page update data of your flat list
And also put a flag which will check when the data has ended which will help to hide the load more button when data ends
Working example: https://snack.expo.io/#msbot01/suspicious-orange
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
SafeAreaView,
SectionList,
Switch,
FlatList
} from 'react-native';
import Constants from 'expo-constants';
import Icon from 'react-native-vector-icons/FontAwesome';
import AwesomeIcon from 'react-native-vector-icons/FontAwesome';
// or any pure javascript modules available in npm
import { Card } from 'react-native-paper';
export default class App extends Component<Props> {
constructor(props) {
super(props);
this.state = {
page:1,
perPage:2,
loadMoreVisible:true,
DATA: [{
id: 'bd7acbea-c1b1-46c2-aed5-3ad53abb28ba',
title: 'First Item',
},
{
id: '3ac68afc-c605-48d3-a4f8-fbd91aa97f63',
title: 'Second Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29d72',
title: 'Third Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29d72',
title: 'fourth Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29d72',
title: 'fifth Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29sd72',
title: 'sixth Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29dr72',
title: 'seventh Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29d7w2',
title: 'Eight Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29ad72',
title: 'Nineth Item',
},
{
id: '58694a0f-3da1-471f-bd96-14557d1e29d72',
title: 'Tenth Item',
}],
displayArray:[]
}
}
componentDidMount(){
this.setNewData()
// console.log(tempArray)
}
setNewData(){
var tempArray=[]
if(this.state.DATA.length == this.state.displayArray.length){
this.setState({
loadMoreVisible:false
})
}else{
for(var i=0; i<(this.state.page*this.state.perPage); i++){
tempArray.push(this.state.DATA[i])
}
this.setState({
displayArray: tempArray,
loadMoreVisible:true
})
}
}
loadMore(){
this.setState({
page: this.state.page+1
},()=>{
this.setNewData()
})
}
render() {
return (
<View style={{ flex: 1 }}>
<FlatList
data={this.state.displayArray}
renderItem={({item})=>
<View style={{flexDirection:'row'}}>
<Text style={{fontSize:20}}>{item.title} </Text>
</View>
}
keyExtractor={item => item.id}
/>
{this.state.loadMoreVisible == true?
<View style={{width:'100%', height:10, backgroundColor:'green', justifyContent:'center', alignItems:'center'}} onClick={()=>{this.loadMore()}}>Load more</View>:null
}
</View>
);
}
}
Set data in state (already done ==> this.state.listData)
Set counter in state (initialize with 1)
Set 15 first elements in state (you can name it "renderedData" or something like that) and then increase cuonter to 1
Add a function that increases the "renderedData" by 15 items by increasing the counter by one
Add Footer component to the list which will call the function you created in stage 3
To take only 15( or 30/45/60 etc..) items from the list you can do something like this:
this.setState({ renderedItem: listData.slice(0, counter*15) })

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>
);
}
}