Cannot update a component (`Categoriess`) while rendering a different component (`CellRenderer`) - javascript

I am getting this warning.
Warning: Cannot update a component (Categoriess) while rendering a different component (CellRenderer). To locate the bad setState() call inside, follow the stack trace as described in https://reactjs.org/link/setstate-in-render%60
I tried too much to fix this issue but I can't. Can anyone please solve this issue?
my code:
`
import { DrawerActions, useNavigation } from '#react-navigation/native';
import { Divider, NativeBaseProvider } from 'native-base';
import fetch from 'node-fetch';
import React, { useEffect, useState } from 'react';
import {
ActivityIndicator,
FlatList,
StyleSheet, Text, TouchableOpacity, View
} from 'react-native';
import AntDesign from 'react-native-vector-icons/AntDesign';
import { API_KEY, URL } from '../../globalVariable';
const Categoriess = () => {
const [data, setData] = useState([]);
const [homeID, setHomeId] = useState([]);
const [loading, setLoading] = useState(true)
const navigation = useNavigation();
const option = () => {
let url = `${URL}/store-api/category`;
let options = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
'sw-access-key': `${API_KEY}`,
},
};
fetch(url, options)
.then(res => res.json())
.then(json => {
setData(!!json?.elements ? json.elements : [])
setLoading(false)
})
.catch(err => { });
};
useEffect(() => {
option();
}, []);
return (
<View >
<TouchableOpacity
onPress={() => navigation.dispatch(DrawerActions.closeDrawer())}>
<View style={styles.closeDrawer}>
<Text style={styles.text}>Close Menu</Text>
<AntDesign name='close' size={30} />
</View>
</TouchableOpacity>
<Text style={{ fontSize: 25, fontWeight: 'bold', textAlign: 'center' }}>
Categories
</Text>
{
loading ? <ActivityIndicator /> : <FlatList
data={data}
initialNumToRender={7}
renderItem={({ item }) => {
{
if (item.level === 1 && item.afterCategoryId === null) {
setHomeId(item.id);
}
}
const newLocal =
item.level === 2 && item.parentId === homeID ? item.parentId : '';
return (
<View >
<View>
<View>
{item.visible === true && item.parentId === newLocal ? (
<View>
<TouchableOpacity
onPress={() => navigation.navigate('Products', item)}>
<Text style={styles.category}>{item.name}</Text>
<NativeBaseProvider>
<Divider thickness={3} bg="red.200" />
</NativeBaseProvider>
</TouchableOpacity>
{data.map((curElem, index) => {
return item.id === curElem.parentId ? (
<TouchableOpacity
key={index}
onPress={() =>
navigation.navigate('Products', curElem)
}>
<Text style={styles.subCategory}>
- {curElem.name}
</Text>
<NativeBaseProvider>
<Divider thickness={1} />
</NativeBaseProvider>
</TouchableOpacity>
) : null;
})
}
</View>
) : null}
</View>
</View>
</View>
);
}}
/>
}
</View>
);
};
export default Categoriess;
const styles = StyleSheet.create({
category: {
marginHorizontal: 15,
marginVertical: 5,
paddingVertical: 10,
fontSize: 18,
color: '#6779b4',
fontFamily: "Montserrat-Bold"
},
subCategory: {
marginLeft: 30,
marginBottom: 10,
fontSize: 15,
paddingVertical: 10,
fontFamily: "Montserrat-Bold"
},
closeDrawer: {
flexDirection: 'row',
justifyContent: 'space-between',
paddingHorizontal: 10,
paddingVertical: 5,
},
text: {
fontSize: 18,
fontFamily: "Montserrat-Bold"
},
});
`

Check this part in renderItem prop. This issue often happens when you trying setState something inside render function
if (item.level === 1 && item.afterCategoryId === null) {
setHomeId(item.id);
}
Looks like you shouldn't store this value in state. Just check it in every renderItem will be enough. FlatList optimisations do all other work well.
const homeID = item.level === 1 && item.afterCategoryId === null ? setHomeId(item.id) : null;
As example

Related

render only 10 item in react native flatlist on each page, then the next 5 on pull to load more item

i want to do paging but i cant limit the item to 10. this code shows all the items.
these are not working as well -initialNumToRender,maxToRenderPerBatch,windowSize
<FlatList
data={DATA}
renderItem={({ item }) => (
<Item title={item.name} />
)}
keyExtractor={item => item.id}
ItemSeparatorComponent={ItemSeparatorView}
initialNumToRender={11}
maxToRenderPerBatch={5}
windowSize={2}
/>
Try this,
import React, {useState, useEffect} from 'react';
//import all the components we are going to use
import {
SafeAreaView,
View,
Text,
TouchableOpacity,
StyleSheet,
FlatList,
ActivityIndicator,
} from 'react-native';
const App = () => {
const [loading, setLoading] = useState(true);
const [dataSource, setDataSource] = useState([]);
const [offset, setOffset] = useState(1);
useEffect(() => getData(), []);
const getData = () => {
console.log('getData');
setLoading(true);
//Service to get the data from the server to render
fetch('https://aboutreact.herokuapp.com/getpost.php?offset='
+ offset)
//Sending the currect offset with get request
.then((response) => response.json())
.then((responseJson) => {
//Successful response
setOffset(offset + 1);
//Increasing the offset for the next API call
setDataSource([...dataSource, ...responseJson.results]);
setLoading(false);
})
.catch((error) => {
console.error(error);
});
};
const renderFooter = () => {
return (
//Footer View with Load More button
<View style={styles.footer}>
<TouchableOpacity
activeOpacity={0.9}
onPress={getData}
//On Click of button load more data
style={styles.loadMoreBtn}>
<Text style={styles.btnText}>Load More</Text>
{loading ? (
<ActivityIndicator
color="white"
style={{marginLeft: 8}} />
) : null}
</TouchableOpacity>
</View>
);
};
const ItemView = ({item}) => {
return (
// Flat List Item
<Text
style={styles.itemStyle}
onPress={() => getItem(item)}>
{item.id}
{'.'}
{item.title.toUpperCase()}
</Text>
);
};
const ItemSeparatorView = () => {
return (
// Flat List Item Separator
<View
style={{
height: 0.5,
width: '100%',
backgroundColor: '#C8C8C8',
}}
/>
);
};
const getItem = (item) => {
//Function for click on an item
alert('Id : ' + item.id + ' Title : ' + item.title);
};
return (
<SafeAreaView style={{flex: 1}}>
<View style={styles.container}>
<FlatList
data={dataSource}
keyExtractor={(item, index) => index.toString()}
ItemSeparatorComponent={ItemSeparatorView}
enableEmptySections={true}
renderItem={ItemView}
ListFooterComponent={renderFooter}
/>
</View>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
justifyContent: 'center',
flex: 1,
},
footer: {
padding: 10,
justifyContent: 'center',
alignItems: 'center',
flexDirection: 'row',
},
loadMoreBtn: {
padding: 10,
backgroundColor: '#800000',
borderRadius: 4,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
},
btnText: {
color: 'white',
fontSize: 15,
textAlign: 'center',
},
});
export default App;

data size bigger than 1 has rendering issue

data that worked looks like this
(I used JSON.stringify function to see how the data looks like)
[{"_id":"612e60c4ce136a1f4454c938", "individualPurchasePrice":9800,"teamPurchasePrice":640, "eventDealCategory":"DysonHairDryer"}]
below is the data that didn't work
[{"_id":"612e60c4ce136a1f4454c938", "individualPurchasePrice":9800,"teamPurchasePrice":640, "eventDealCategory":"JMWHairDryer"}, {"_id":"612e60c4ce136a1f4454c938", "individualPurchasePrice":9800,"teamPurchasePrice":640, "eventDealCategory":"JMWHairDryer"},]
this is the error message
Element type is invalid:expected a string (for built-in components) or a class/function (for composite components) but got: object.
import React from 'react'
import { SafeAreaView, View, Dimensions, Text, ScrollView } from 'react-native'
import ItemStore from '../stores/ItemStore'
import ImageManager from '../images/ImageManager'
import ItemListWithoutCategory from '../components/item/ItemListWithoutCategory'
import TimeDealItemComponent from '../components/item/TimeDealItemComponent'
const dims = Dimensions.get('window')
const TimeDealItemScreen = ({ route, navigation }) => {
const { eventDealCategoryName } = route.params
return (
<SafeAreaView style={{ flex: 1 }}>
<ScrollView style={{ height: 40, backgroundColor: 'red' }}>
<ImageManager
source='TimeDealGradientImage'
style={{
position: 'absolute',
width: '100%',
height: '100%',
top: 0,
bottom: 0,
left: 0,
right: 0,
}}
/>
<ItemListWithoutCategory
isFrom='TimeDealItemScreen'
ItemSeparatorComponent={<View style={{ height: 16 }} />}
ListFooterComponent={
<>
<View style={{ height: 16, backgroundColor: 'transparent' }} />
</>
}
data={ItemStore?.eventDealItems?.filter(
(item) => item.eventDealCategory === eventDealCategory,
)}
renderItem={({ item, index }) => (
<View style={{ paddingHorizontal: 8 }}>
<TimeDealItemComponent item={item} index={index} />
</View>
)}
ListFooterComponent={
<>
<View style={{ height: 16, backgroundColor: 'transparent' }} />
</>
}
/>
</ScrollView>
</SafeAreaView>
)
}
export default TimeDealItemScreen
import React, { useState, useRef, useEffect, useCallback } from 'react'
import { FlatList } from 'react-native'
import ItemStore from '../../stores/ItemStore'
import { observer } from 'mobx-react-lite'
import UserStore from '../../stores/UserStore'
import { useNavigation } from '#react-navigation/native'
import viewabilityConfig from '../config/viewabilityConfig'
const ItemListWithoutCategory = observer(
({
ListEmptyComponent,
ListFooterComponent,
ItemSeparatorComponent,
data,
renderItem,
ListHeaderComponent,
isFrom,
ref,
onEndReached,
numColumns,
}) => {
const navigation = useNavigation()
const onViewableItemsChanged = useCallback(
({ viewableItems, changed }) => {
changed.forEach((item) => {
if (item.isViewable) {
const addedImpressionItems = {
itemId: item.item?._id,
itemTitle: item.item?.itemTitle,
loggedAt: new Date(),
isFrom,
}
ItemStore.addImpressionItems(addedImpressionItems)
}
})
},
[ItemStore.screenOnFocusMyOrder],
)
const viewabilityConfigCallbackPairs = useRef([
{ viewabilityConfig, onViewableItemsChanged },
])
return (
<FlatList
ref={ref}
viewabilityConfigCallbackPairs={viewabilityConfigCallbackPairs.current}
data={data}
ListHeaderComponent={ListHeaderComponent}
renderItem={renderItem}
keyExtractor={(item, index) => item?._id + index.toString()}
ListEmptyComponent={ListEmptyComponent}
ListFooterComponent={ListFooterComponent}
ItemSeparatorComponent={ItemSeparatorComponent}
onEndReached={onEndReached}
numColumns={numColumns}
/>
)
},
)
export default ItemListWithoutCategory
import React, { useState, useEffect } from 'react'
import { View, TouchableOpacity, Dimensions } from 'react-native'
import BlackText from '../texts/BlackText'
import GrayText from '../texts/GrayText'
import BasicButton from '../buttons/BasicButton'
import { observer } from 'mobx-react-lite'
import UserStore from '../../stores/UserStore'
import OrderStore from '../../stores/OrderStore'
import ImageManager from '../../images/ImageManager'
import backendApis from '../../utils/backendApis'
import { useNavigation } from '#react-navigation/native'
import ItemStore from '../../stores/ItemStore'
import RedText from '../texts/RedText'
import FastImage from 'react-native-fast-image'
import ImageTextTimer from '../texts/ImageTextTimer'
import commaNumber from 'comma-number'
const dims = Dimensions.get('window')
const TimeDealItemComponent = observer(({ item, index }) => {
const FULL_GAGE_WIDTH = 70
const CURRENT_GAGE_WIDTH = item.stockSoldPercentage
const GAGE_HEIGHT = 16
const navigation = useNavigation()
const TRANSPARENT_GRAY_CIRCLE_SIZE = 80
const [eventDealStatusHere, setEventDealStatusHere] = useState(0)
const [orderRecord, setOrderRecord] = useState('unpurchased')
useEffect(() => {
const eventStartedDate = new Date(item.eventDealStartedAt) // x
const now = new Date().getTime() // y
const oneDayTerm = 1000 * 60 * 60 * 24 * 1 // 7일
const stockleft = item.eventDealStock - item.totalOrderQuantity
if (eventStartedDate > new Date(now)) {
setEventDealStatusHere('preOpened')
} else if (
eventStartedDate < new Date(now) &&
eventStartedDate > new Date(now - oneDayTerm) &&
stockleft > 0
) {
setEventDealStatusHere('opened')
} else if (
eventStartedDate < new Date(now) &&
eventStartedDate > new Date(now - oneDayTerm) &&
stockleft <= 0
) {
setEventDealStatusHere('temporarilyClosed')
} else setEventDealStatusHere('closed')
}, [])
useEffect(() => {
if (
OrderStore.loadedUserOrdersList.find(
(order) =>
[
'pre-shipping',
'shipping',
'exchanging',
'arrived',
'reviewed',
].includes(order.status) && item._id === order.itemInfo.itemId,
)
) {
setOrderRecord('purchased')
} else {
setOrderRecord('unpurchased')
}
}, [OrderStore.loadedUserOrdersList])
const StockInfoTextComponent = ({ text }) => {
return (
<View
style={{
flexDirection: 'row',
alignContent: 'center',
// backgroundColor: 'grey',
}}
>
<GrayText text={text} fontSize={14} dark numberOfLines={1} />
</View>
)
}
const StockInfoText = () => {
if (eventDealStatusHere === 'preOpened') {
return (
<StockInfoTextComponent
text={`${commaNumber(item.eventDealStock)}개 입고 예정`}
/>
)
}
if (eventDealStatusHere === 'temporarilyClosed') {
return <StockInfoTextComponent text='일시적 물량 소진' />
}
if (eventDealStatusHere === 'closed') {
return <StockInfoTextComponent text='재고 전량 소진' />
}
return <></>
}
return (
<View style={{ width: '100%' }}>
<View
style={{
flex: 1,
width: '100%',
}}
>
<TouchableOpacity
style={{
backgroundColor: 'white',
marginTop: 12,
padding: 8,
borderRadius: 8,
}}
activeOpacity={1.0}
onPress={() => {
if (ItemStore.isLoadingItemScreen) {
return
}
ItemStore.setIsLoadingItemScreen(true)
navigation.push('MainStackDItemScreen', {
itemId: item._id,
itemInfo: item.itemInfo,
enteringComponent: 'TimeDealItemComponent',
})
setTimeout(() => {
ItemStore.setIsLoadingItemScreen(false)
}, 1000)
}}
>
<View
style={{
flexDirection: 'row',
marginTop: 4,
borderRadius: 4,
}}
>
<View
style={{
flex: 2,
flexDirection: 'column',
paddingHorizontal: 8,
}}
>
<View
style={{
paddingBottom: 8,
flexDirection: 'row',
paddingRight: 16,
}}
>
<BlackText
text={item.itemTitle}
fontSize={16}
numberOfLines={2}
/>
</View>
{/* 1.아이템타이틀 끝 */}
{/* 7. 게이지 시작 */}
<View
style={{
alignContent: 'center',
justifyContent: 'center',
}}
>
{eventDealStatusHere === 'opened' && (
<View
style={{
flexDirection: 'row',
alignItems: 'center',
}}
>
<View
style={{
backgroundColor: '#E89FA1',
height: GAGE_HEIGHT,
width: FULL_GAGE_WIDTH,
borderRadius: 12,
}}
>
<View
style={{
backgroundColor: '#EC4F48',
height: GAGE_HEIGHT,
width: FULL_GAGE_WIDTH * CURRENT_GAGE_WIDTH,
borderRadius: 12,
}}
/>
</View>
{/* opened */}
{eventDealStatusHere === 'opened' && (
<View
style={{
flexDirection: 'row',
alignContent: 'center',
paddingLeft: 8,
// backgroundColor: 'grey',
}}
>
<GrayText
text={item.stockLeft}
fontSize={12}
numberOfLines={1}
/>
<GrayText
text='개 남음'
fontSize={12}
numberOfLines={1}
/>
</View>
)}
</View>
)}
</View>
{/* 7. 게이지 끝 */}
{orderRecord === 'purchased' && (
<View
style={{
borderRadius: 4,
paddingTop: 10,
}}
>
<>
<BasicButton
width={30}
text='이미 구매하신 아이템입니다.'
eventDealClosed
backgroundColor='#B3B4B7'
/>
</>
</View>
)}
</TouchableOpacity>
</View>
</View>
)
})
export default TimeDealItemComponent
below is the data that didn't work
[{"_id":"612e60c4ce136a1f4454c938", "eventDealCategory":"JMWHairDryer"}, {"_id":"612e60c4ce136a1f4454c938", "eventDealCategory":"JMWHairDryer"},]
It does not work in second example because instead of one object you have array of objects. To render them you need to map throught array.

Check the render method of `RestApi`

i got this This error
Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
Check the render method of RestApi.
This is my code App.js
import React from 'react';
import { StyleSheet } from 'react-native';
import RestApi from './x/RestApi';
export default function App() {
return (
<RestApi/>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
color: 'white',
alignItems: 'center',
justifyContent: 'center',
}
})
This is my code RestApi.js
import axios from 'axios';
import React, { useState, useEffect } from 'react'
import { Button, Flatlist, SafeAreaView, ScrollView, StyleSheet, Text, TextInput, TouchableOpacity, View } from 'react-native';
export default function RestApi() {
const [title, setTitle] = useState("");
const [value,setValue] = useState("");
const [items, setItems] = useState([]);
const [button, setButton] = useState("Simpan");
const [selectedUser, setSelectedUser] = useState({});
const submit =()=>{
const data = {
value,title
}
//console.log(data)
if(button=="Simpan"){
axios.post('https://achmadhilmy-sanbercode.my.id/api/v1/news', data)
.then(res=>{
console.log('res: ', res)
setTitle("")
setValue("")
GetData()
}).catch(err=>{
console.log('error: ',err)
})
}else{
axios.put(`https://achmadhilmy-sanbercode.my.id/api/v1/news/${selectedUser.id}`, data)
.then(res=>{
console.log('res: ',res)
setTitle("")
setValue("")
GetData()
setButton("Simpan")
}).catch(err=>{
console.log('error: ', err)
})
}
}
const onSelectItem = (item)=>{
console.log(item)
setSelectedUser(item)
setTitle(item.title)
setValue(item.value)
setButton("Update")
}
const GetData=()=>{
axios.get('https://achmadhilmy-sanbercode.my.id/api/v1/news')
.then(res=>{
const data1 = (res.data.data)
console.log('res: ', data1)
setItems(data1)
})
}
const onDelete=(item)=>{
axios.delete(`https://achmadhilmy-sanbercode.my.id/api/v1/news/${item.id}`)
.then(res=>{
console.log('res: ', res)
GetData()
}).catch(err=>{
console.log('error: ', err)
})
}
useEffect(() => {
GetData()
}, [])
return (
<View style={styles.container}>
<View style={styles.header}>
<Text style={styles.title}>Tampilkan Api (CRUD)</Text>
</View>
<View style={styles.content1}>
<Text>Post Data</Text>
<TextInput
placeholder="Masukan judul berita"
style={styles.input}
value={title}
onChangeText={(value)=>setTitle(value)}
/>
<TextInput
placeholder="Masukan Isi berita"
style={styles.input}
value={value}
onChangeText={(value)=>setValue(value)}
/>
<Button
title={button}
onPress={submit}
/>
</View>
<View style={styles.content1}>
<Text>Get Data Berita</Text>
<Flatlist
data={items}
keyExtractor={(item, index) => `${item.id}-${index}`}
renderItem={({item})=>{
return (
<View>
<TouchableOpacity onPress={()=>onDelete(item)}>
<Text style={{color: 'red', alignSelf: 'flex-end'}}>X</Text>
</TouchableOpacity>
<TouchableOpacity onPress={()=>onSelectItem(item)}style={{borderRadius: 6,backgroundColor:'grey', padding: 5, marginBottom:10}}>
<Text style={{color: 'white'}}>{item.title}</Text>
<Text style={{color: 'white'}}>{item.value}</Text>
</TouchableOpacity>
</View>
)
}}
/>
</View>
</View>
)
}
const styles = StyleSheet.create({
container:{
flex:1,
backgroundColor:'white'
},
header:{
paddingTop: 50,
paddingHorizontal: 16,
backgroundColor: 'grey',
alignItems: 'center'
},
title:{
color: 'white',
fontSize: 20
},
content1:{
paddingHorizontal: 16
},
input:{
borderWidth:1,
paddingVertical: 10,
paddingHorizontal: 5,
borderRadius: 6,
marginTop: 10
},
contentNews:{
backgroundColor:'grey',
paddingVertical:10
}
})
Your RestApi component does return anything, because the return statement is nested within the submit function, but that does not get called.
Could you try unnesting the return statement? See example below, where I also unnested the useEffect call and several other functions.
import axios from 'axios'
import React, { useState, useEffect } from 'react'
import {
Button,
Flatlist,
SafeAreaView,
ScrollView,
StyleSheet,
Text,
TextInput,
TouchableOpacity,
View
} from 'react-native'
export default function RestApi() {
const [title, setTitle] = useState('')
const [value, setValue] = useState('')
const [items, setItems] = useState([])
const [button, setButton] = useState('Simpan')
const [selectedUser, setSelectedUser] = useState({})
const submit = () => {
const data = {
value,
title
}
//console.log(data)
if (button == 'Simpan') {
axios
.post('https://achmadhilmy-sanbercode.my.id/api/v1/news', data)
.then(res => {
console.log('res: ', res)
setTitle('')
setValue('')
GetData()
})
.catch(err => {
console.log('error: ', err)
})
} else {
axios
.put(
`https://achmadhilmy-sanbercode.my.id/api/v1/news/${selectedUser.id}`,
data
)
.then(res => {
console.log('res: ', res)
setTitle('')
setValue('')
GetData()
setButton('Simpan')
})
.catch(err => {
console.log('error: ', err)
})
}
}
const onSelectItem = item => {
console.log(item)
setSelectedUser(item)
setTitle(item.title)
setValue(item.value)
setButton('Update')
}
const GetData = () => {
axios
.get('https://achmadhilmy-sanbercode.my.id/api/v1/news')
.then(res => {
const data1 = res.data.data
console.log('res: ', data1)
setItems(data1)
})
}
const onDelete = item => {
axios
.delete(
`https://achmadhilmy-sanbercode.my.id/api/v1/news/${item.id}`
)
.then(res => {
console.log('res: ', res)
GetData()
})
.catch(err => {
console.log('error: ', err)
})
}
useEffect(() => {
GetData()
}, [])
return (
<View style={styles.container}>
<View style={styles.header}>
<Text style={styles.title}>Tampilkan Api (CRUD)</Text>
</View>
<View style={styles.content1}>
<Text>Post Data</Text>
<TextInput
placeholder="Masukan judul berita"
style={styles.input}
value={title}
onChangeText={value => setTitle(value)}
/>
<TextInput
placeholder="Masukan Isi berita"
style={styles.input}
value={value}
onChangeText={value => setValue(value)}
/>
<Button title={button} onPress={submit} />
</View>
<View style={styles.content1}>
<Text>Get Data Berita</Text>
<Flatlist
data={items}
keyExtractor={(item, index) => `${item.id}-${index}`}
renderItem={({ item }) => {
return (
<View>
<TouchableOpacity
onPress={() => onDelete(item)}>
<Text
style={{
color: 'red',
alignSelf: 'flex-end'
}}>
X
</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => onSelectItem(item)}
style={{
borderRadius: 6,
backgroundColor: 'grey',
padding: 5,
marginBottom: 10
}}>
<Text style={{ color: 'white' }}>
{item.title}
</Text>
<Text style={{ color: 'white' }}>
{item.value}
</Text>
</TouchableOpacity>
</View>
)
}}
/>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'white'
},
header: {
paddingTop: 50,
paddingHorizontal: 16,
backgroundColor: 'grey',
alignItems: 'center'
},
title: {
color: 'white',
fontSize: 20
},
content1: {
paddingHorizontal: 16
},
input: {
borderWidth: 1,
paddingVertical: 10,
paddingHorizontal: 5,
borderRadius: 6,
marginTop: 10
},
contentNews: {
backgroundColor: 'grey',
paddingVertical: 10
}
})

How to save route.params with asyncstorage?

Srry if the title makes no sense. Don't know a better title.
How can I save route.params items that I pass to my second screen using AsyncStorage?
In my first screen i have a bunch of data in a FlatList that can be opened with a Modal. Inside that Modal I have a TouchableOpacity that can send the data thats inside the Modal to my second screen. The data that has been passed to the second screen is passed to a FlatList. The data in the FlatList should be saved to AsyncStorage. Tried alot of things getting this to work, but only getting warning message
undefined. Code below is the most recent progress.
Using React Navigation V5.
FIRST SCREEN
const [masterDataSource, setMasterDataSource] = useState(DataBase);
const [details, setDetails] = useState('');
<TouchableOpacity
onPress={() => {
const updated = [...masterDataSource];
updated.find(
(item) => item.id === details.id,
).selected = true;
setMasterDataSource(updated);
navigation.navigate('cart', {
screen: 'cart',
params: {
items: updated.filter((item) => item.selected),
},
});
setModalVisible(false);
}}>
<Text>Add to cart</Text>
</TouchableOpacity>
SECOND SCREEN
import React, { useEffect, useState } from 'react';
import { View, Text, FlatList, TouchableOpacity } from 'react-native';
import { useTheme } from '../Data/ThemeContext';
import AsyncStorage from '#react-native-async-storage/async-storage';
import Ionicons from 'react-native-vector-icons/Ionicons';
export default function ShoppingList({ route, navigation }) {
const [shoppingList, setShoppingList] = useState([]);
const { colors } = useTheme();
const todo = () => {
alert('Todo');
};
useEffect(() => {
restoreShoppingListAsync();
}, []);
const shoppingListAsync = () => {
const shoppingList = route.params && route.params.items;
setShoppingList(list);
storeShoppingList(list);
};
const asyncStorageKey = '#ShoppingList';
const storeShoppingListAsync = (list) => {
const stringifiedList = JSON.stringify(list);
AsyncStorage.setItem(asyncStorageKey, stringifiedList).catch((err) => {
console.warn(err);
});
};
const restoreShoppingListAsync = () => {
AsyncStorage.getItem(asyncStorageKey)
.then((stringifiedList) => {
console.log(stringifiedList);
const parsedShoppingList = JSON.parse(stringifiedList);
if (!parsedShoppingList || typeof parsedShoppingList !== 'object')
return;
setShoppingList(parsedShoppingList);
})
.then((err) => {
console.warn(err);
});
};
const RenderItem = ({ item }) => {
return (
<View>
<TouchableOpacity
style={{
marginLeft: 20,
marginRight: 20,
elevation: 3,
backgroundColor: colors.card,
borderRadius: 10,
}}>
<View style={{ margin: 10 }}>
<Text style={{ color: colors.text, fontWeight: '700' }}>
{item.name}
</Text>
<Text style={{ color: colors.text }}>{item.gluten}</Text>
<Text style={{ color: colors.text }}>{item.id}</Text>
</View>
</TouchableOpacity>
</View>
);
};
const emptyComponent = () => {
return (
<View style={{ alignItems: 'center' }}>
<Text style={{ color: colors.text }}>Listan är tom</Text>
</View>
);
};
const itemSeparatorComponent = () => {
return (
<View
style={{
margin: 3,
}}></View>
);
};
return (
<View
style={{
flex: 1,
}}>
<View
style={{
padding: 30,
backgroundColor: colors.Textinput,
elevation: 12,
}}>
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
<TouchableOpacity onPress={() => navigation.goBack()}>
<Ionicons name="arrow-back-outline" size={25} color="#fff" />
</TouchableOpacity>
<Text style={{ color: '#fff', fontSize: 20 }}>Inköpslista</Text>
<TouchableOpacity>
<Ionicons
name="trash-outline"
size={25}
color="#fff"
onPress={() => todo()}
/>
</TouchableOpacity>
</View>
</View>
<View style={{ flex: 1, marginTop: 30 }}>
<FlatList
data={shoppingList}
renderItem={RenderItem}
ListEmptyComponent={emptyComponent}
ItemSeparatorComponent={itemSeparatorComponent}
initialNumToRender={4}
maxToRenderPerBatch={5}
windowSize={10}
removeClippedSubviews={true}
updateCellsBatchingPeriod={100}
showsVerticalScrollIndicator={true}
contentContainerStyle={{ paddingBottom: 20 }}
/>
</View>
</View>
);
}
As you are using async storage to maintain the cart.
I would suggest an approach as below
Update the asyn storage when new items are added to or removed from the cart
Retrieve the items from the cart screen and show the items there
Before you navigate store the items like below
AsyncStorage.setItem(
'Items',
JSON.stringify(updated.filter((item) => item.selected))
).then(() => {
navigation.navigate('Cart', {
items: updated.filter((item) => item.selected),
});
});
The cart screen would be something like below
function Cart({ navigation, route }) {
const [data,setData]=useState([]);
React.useEffect(() => {
async function fetchMyAPI() {
const value = await AsyncStorage.getItem('Items');
setData(JSON.parse(value));
}
fetchMyAPI();
}, []);
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button title="Go back" onPress={() => navigation.goBack()} />
<FlatList
data={data}
renderItem={RenderItem}
/>
</View>
);
}
Working Example
https://snack.expo.io/#guruparan/cartexample

How can i get back my data list after searching by words?

i use Flatlist and search by words on renderHeader() function. I can filter but when i delete letters, i couldn't get main list again. I think there is a logic problem but i couln't find after trying something...
i've got a new one when i fixed problem. I tried to fix but i couldn't do that, i should put the problem in front of experts :)
import React, {Component, useState} from 'react'
import {
Text,
StyleSheet,
View,
FlatList,
SafeAreaView,
ScrollView,
Image,
TextInput,
} from 'react-native'
import data from '../../data'
const Flatlistexample = () => {
//main list state
let [list, setList] = useState(data);
//search state
const [search, setSearch] = useState('');
//search filter
searchFilter = text => {
// onChangeText
const newData = list.filter(item => {
const listItem = `${item.name.toLowerCase()} ${item.company.toLowerCase()}`
return listItem.indexOf(text.toLowerCase()) > -1;
})
setList(newData)
}
//search function
renderHeader = () =>{
return (
<View style={styles.seachContainer}>
<TextInput
style={styles.textInput}
placeholder={'Search...'}
value={search}
onChangeText={text => {
//setStates
searchFilter(text)
setSearch(text)
}}></TextInput>
<Text
style={{
alignItems: 'flex-start',
color: 'black',
fontSize: 22,
}}>
{search}
</Text>
</View>
)
}
return (
<SafeAreaView
style={{
flex: 1,
}}>
<FlatList
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}
//called search function
ListHeaderComponent={renderHeader()}
/>
</SafeAreaView>
)
}
export default Flatlistexample
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,
},
})
Thanks for your help
Filter Data
onSearchText = (value) => {
this.setState({searchText: value})
if(value.trim() == "" || value == null){
this.setState({list: this.state.list}
} else {
let filter = this.state.list.fillter(data => {
// data fillter logic //
return data;
})
this.setState({filterData: filter})
}
Render FlatList
<FlatList
extradata={this.state}
data={searchText ? filterData : list}
/>
I fixed...
How?
My main data state is constant, i'm filtering on data list with filter state. So my data list doesn't change anytime.
import React, {Component, useState} from 'react'
import {
Text,
StyleSheet,
View,
FlatList,
SafeAreaView,
ScrollView,
Image,
TextInput,
} from 'react-native'
import data from '../../data'
const Flatlistexample = () => {
//main list state
let [list, setList] = useState(data)
//search state
const [search, setSearch] = useState('')
//filter state
const [updated, setUpdated] = useState(data)
//search filter
searchFilter = text => {
// onChangeText
if (text) {
const newData = list.filter(item => {
const listItem = `${item.name.toLowerCase()} ${item.company.toLowerCase()}`
return listItem.indexOf(text.toLowerCase()) > -1
})
setUpdated(newData)
}
//search function
renderHeader = () => {
return (
<View style={styles.seachContainer}>
<TextInput
style={styles.textInput}
placeholder={'Search...'}
value={search}
onChangeText={text => {
searchFilter(text)
setSearch(text)
}}></TextInput>
<Text
style={{
alignItems: 'flex-start',
color: 'black',
fontSize: 22,
}}>
{search}
</Text>
</View>
)
}
return (
<SafeAreaView
style={{
flex: 1,
}}>
<FlatList
//i'm showing filter state
data={updated}
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}
//called search function
ListHeaderComponent={renderHeader()}
/>
</SafeAreaView>
)
}
export default Flatlistexample
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,
},
})
/*
else if(text.length > uzunluk){
setList(data)
const newData = list.filter(item => {
const listItem = `${item.name.toLowerCase()} ${item.company.toLowerCase()}`
return listItem.indexOf(text.toLowerCase()) > -1;
})
setList(newData)
}else if(text.length<uzunluk){
setList(data)
const newData = list.filter(item => {
const listItem = `${item.name.toLowerCase()} ${item.company.toLowerCase()}`
return listItem.indexOf(text.toLowerCase()) > -1;
})
setList(newData)
}
*/

Categories

Resources