sorry for bad English.
My function component not waiting API Function, I'm write async and await and not working again..
"Object are not valid as a React child" error screen...
Please help me :'/
const NormalCarousel = async (props) => {
const navigation = useNavigation();
const [ResponseData, setResponseData] = useState('');
const ComponentAPI = props.api;
const API = await axios.get(ComponentAPI).catch((error) => {alert(error)});
await setResponseData(API);
return(
<ScrollView horizontal={true} showsHorizontalScrollIndicator={false}>
{
ResponseData.map(item => (
<TouchableOpacity style={styles.CarouselTouchable} onPress={() => navigation.navigate("Ürün", {id: item.item_id})}>
<Image
style={styles.CarouselImage}
source={{uri: item?.item_avatar}}
/>
<View style={styles.CarouselView}>
<Text style={styles.CarouselTitle}>{item?.item_name}</Text>
<Text style={styles.CarouselSubtitle}>{item?.item_stock_code}</Text>
</View>
</TouchableOpacity>
))
}
</ScrollView>
)
}
The error is happening because the parent component trying to render this component is actually rendering a Promise which resolves into a component. That's not possible.
You should instead call the function to load the data once on component mount (useEffect). You'll also need to replace useState('') with useState([]) since you're trying to map over this data when rendering.
const NormalCarousel = (props) => {
const { api } = props;
const navigation = useNavigation();
const [responseData, setResponseData] = useState([]);
useEffect(() => {
getAPI();
}, []);
async function getAPI() {
const API = await axios.get(api).catch((error) => alert(error));
setResponseData(API);
}
return(
<ScrollView horizontal showsHorizontalScrollIndicator={false}>
{
responseData.map(item => (
<TouchableOpacity style={styles.CarouselTouchable} onPress={() => navigation.navigate("Ürün", {id: item.item_id})}>
<Image
style={styles.CarouselImage}
source={{uri: item.item_avatar}}
/>
<View style={styles.CarouselView}>
<Text style={styles.CarouselTitle}>{item.item_name}</Text>
<Text style={styles.CarouselSubtitle}>{item.item_stock_code}</Text>
</View>
</TouchableOpacity>
))
}
</ScrollView>
)
}
Related
How to send data in previous screen with help of goBack() ?
I tried already. For ex: this.props.navigation.navigate('Login', {name: this.state.name})
If you simply want to pass data to the previous screen then the below works:
Parent screen:
const Parent = () => {
const [data, setData] = useState("");
let navigation = useNavigation();
const onPress = () => {
navigation.navigate('Second', { getData: (x)=>setData(x) }); //<---here you have to pass callback function
};
return (
<View style={{ flex: 1 }}>
<Button
onPress={onPress}
title="Add title here"
color="#841584"
disabled={false}
/>
<Text>{data}</Text>
</View>
);
};
Child screen:
const Child = ({ route }) => { //<------here need to take route
let navigation = useNavigation();
const onPress = () => {
let data = "John";
route.params.getData(data); //<-----this way to update.
navigation.goBack();
};
return (
<View style={{ flex: 1 }}>
<Button
onPress={onPress}
title="Add title here"
color="#841584"
disabled={false}
/>
</View>
);
};
I have to press the buttons twice to update the filter value for how I want to display the movies in my app. This is my code:
const HomeScreen = () => {
const navigation = useNavigation();
const [movies, setMovies] = useState({});
useEffect(() => {
getMovies();
},[])
useEffect(() => {
getMoviesFiltered(filter);
},[filter])
const [filter, setFilter] = useState('name');
const getMovies = async (filter) =>{
const querySnapshot = await getDocs(query(collection(db, "movies"), orderBy(filter)));
setMovies(querySnapshot.docs);
}
return (
<View>
<View>
<TouchableOpacity onPress = {() => {setFilter('name'); getMovies(filter)}}>
<Text>Title</Text>
</TouchableOpacity>
<TouchableOpacity onPress = {() => {setFilter('runtime'); getMovies(filter)}}>
<Text>Runtime</Text>
</TouchableOpacity>
<TouchableOpacity onPress = {() => {setFilter('year'); getMovies(filter)}}>
<Text>Year</Text>
</TouchableOpacity>
</View>
</View>
<FlatList
data = {movies}
renderItem = {({item}) => (
<TouchableOpacity onPress={() => navigation.navigate('Modal', item.data())}>
<Image source={{uri: item.data().pic}}/>
</TouchableOpacity>
)
}
/>
</View>
)
}
export default HomeScreen
I know that setState is asynchronous and that that is the reason it happens, but I'm kind of stuck on not knowing how to change it properly, so I'd appreaciate the help. Thank you.
useEffect will run when the values within dependency array change.
So, you don't need to add function getMovies after you changed your filter. Just simply move that to useEffect.
const HomeScreen = () => {
const navigation = useNavigation();
const [movies, setMovies] = useState({});
const [filter, setFilter] = useState('name');
useEffect(() => {
//when page is initialize, run this
getMovies(filter);
},[])
useEffect(() => {
//when filter is changed, use latest value to run this
getMovies(filter);
},[filter]);
const getMovies = async (filter) =>{
const querySnapshot = await getDocs(query(collection(db, "movies"), orderBy(filter)));
setMovies(querySnapshot.docs);
}
return (
<View>
<View>
<TouchableOpacity onPress = {() => {setFilter('name');}}>
<Text>Title</Text>
</TouchableOpacity>
<TouchableOpacity onPress = {() => {setFilter('runtime');}}>
<Text>Runtime</Text>
</TouchableOpacity>
<TouchableOpacity onPress = {() => {setFilter('year');}}>
<Text>Year</Text>
</TouchableOpacity>
</View>
<FlatList
data = {movies}
renderItem = {({item}) => (
<TouchableOpacity onPress={() => navigation.navigate('Modal', item.data())}>
<Image source={{uri: item.data().pic}}/>
</TouchableOpacity>
)
}
/>
</View>
)
}
export default HomeScreen
I am having a problem that I can't solve by my own. I am making an app for making lists in React Native, and in my main screen, that shows the session initiated by the user, I have to render all the lists that he had saved previously. Here is the code of my session component.
export default function Session({navigation,route}){
const {user} = useContext(myContext)
const [modalVisible, setModalVisible] = useState(false)
const [lists, setLists] = useState(route.params.lists)
let keyListCounter = 0
const handleButton = async () => {
await AsyncStorage.removeItem("token")
navigation.navigate("Login")
}
const updateList = (title,newElement) => {
axios.put(`http://${REACT_APP_BACK_URI}/api/lists/add-list-element`, {nickname: user,title,element: newElement})
.then(res => {
if (res.status == 200) {
setLists(res.data.userLists)
}
})
.catch(err => console.log(err))
}
useEffect(() => {
navigation.setOptions({
title: user,
headerTitleAlign: "left",
headerRight: () => (
<TouchableWithoutFeedback onPress={() => handleButton()}>
<Text style={styles.logoutText}>Logout</Text>
</TouchableWithoutFeedback>
)
})
},[navigation,user])
return (
<View style={styles.mainContainerView}>
<ScrollView style={styles.mainContainerScrollView}>
<View style={styles.textListContainer}>
<Text style={styles.listsText}>LISTAS ACTIVAS: </Text>
<Text style={styles.numberListsText}>{lists.length}</Text>
</View>
{lists.map(elem => <List key={keyListCounter++} list={elem} updateList={updateList}/>)}
</ScrollView>
<Pressable style={styles.newListPressable} onPressIn={() => setModalVisible(true)}>
<Text style={styles.newListText}>+</Text>
</Pressable>
<View style={styles.centeredView}>
<Modal
visible={modalVisible}
animationType="slide"
transparent={true}
>
<View style={styles.centeredView}>
<View style={styles.modalView}>
<Text>MODAL</Text>
</View>
</View>
</Modal>
</View>
</View>
)
}
My question is why after I actualize the state of "lists", whose elements are passed as props to the List component, the virtualized list that I have in the List component does not re-renderizes automaticaly.
Here I show also the code of the List component.
export default function List({list,updateList}){
const {elements, title} = list
let elementId = 0
const virtualizedList = useRef()
const [showVirtualizedList, setShowVirtualizedList] = useState("none")
const [showDownArrow, setShowDownArrow] = useState(true)
const [showUpArrow, setShowUpArrow] = useState(false)
let [newElementArray, setNewElementArray] = useState([])
let [listElements, setListElements] = useState(elements)
const getItem = (item) => ({
id: elementId++,
title: item
});
//List Pressable Events
const handlePressIn = () => {
if (showVirtualizedList == "none") setShowVirtualizedList("flex")
else setShowVirtualizedList("none")
setShowDownArrow(!showDownArrow)
setShowUpArrow(!showUpArrow)
}
//New element Pressable Events
const handleNewElement = () => {
setNewElementArray([...newElementArray,uuid.v4()])
}
//NewListItem TouchableWithoutFeedback Events
const deleteElementInput = newItemID => {
const elementsArray = newElementArray.filter(elem => elem != newItemID)
setNewElementArray(elementsArray)
}
const addListElement = (newElement,newItemID) => {
updateList(title,newElement)
deleteElementInput(newItemID)
}
useEffect(() => {
virtualizedList.current.setNativeProps({display: showVirtualizedList})
LogBox.ignoreLogs(['VirtualizedLists should never be nested']);
},[showVirtualizedList, virtualizedList])
return (
<ScrollView style={styles.mainContainer}>
<Pressable
style={styles.listElement}
onPressIn={() => handlePressIn()}
>
<View style={styles.titleContainer}>
<Text style={styles.listElementText}>{title} </Text>
<Text style={styles.listElementQuantity}>({listElements.length})</Text>
</View>
<View>
<DownArrow show={showDownArrow}/>
<UpArrow show={showUpArrow}/>
</View>
</Pressable>
<View>
<VirtualizedList
data={listElements}
initialNumToRender={10}
getItemCount={() => listElements.length}
renderItem={({item}) => <ListItem item={item}/>}
getItem={() => getItem(listElements[elementId])}
ref={virtualizedList}
/>
</View>
{newElementArray.length > 0 ? newElementArray.map(elem => {
return (
<NewListItem
key={elem}
id={elem}
newElementArray={newElementArray}
deleteElementInput={deleteElementInput}
addListElement={addListElement}
/>
)
}) : ""
}
<Pressable style={styles.newElementPressable} onPressIn={() => handleNewElement()}>
<Text style={styles.newElementText}>+</Text>
</Pressable>
</ScrollView>
)
}
UPDATE: I solve the problem using a FlatList instead of a VirtualizedList. For some reason the FlatList re-renders when the Item is updated and the VirtualizedList no. I don't know why.....
I'm sure its a stupid mistake somewhere but when I switch between class component to functional (to learn/understand how state works in both of these) I kind of miss the logic sometimes (with this.props etc). (home.js navigates to the page called addDiary.js)
I'm not finished with the async logic/code but don't understand why I get the error "cant find variable: diary" at this point, thank you
Home.js
const Home = ({navigation}) => {
const [refreshing, setRefreshing] = useState(false);
const [diary, setDiary] = useState(null)
whenRefresh = async () => {
try{
setRefreshing(true);
const diary = await AsyncStorage.getItem('diary')
setDiary(JSON.parse('diary'))
setRefreshing(false)}
catch (error) {console.log(error)}
}
return(
<View style={styles.home}>
<ScrollView
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={whenRefresh}/>}
style={styles.body}>
{diary ? <ListItem
title={diary.title}
subtitle="test"
onPress={()=>{console.log("listitem pressed")}}
/> : null}
addDiary.js
const AddDiary = () => {
const [title, setTitle] = useState()
const [body, setBody] = useState()
const submit = async () => {
const diary = {title, body}
await AsyncStorage.setItem('diary', JSON.stringify(diary))
navigation.goBack()
}
return(
<SafeAreaView style={styles.home}>
<View style={styles.group}>
<Text style={styles.label}>Title:</Text>
<TextInput
placeholder="title"
style={styles.titleInput}
onChangeText={setTitle}
value={title}
/>
</View>
<View style={[styles.group, {flex:1}]}>
<Text style={styles.label}>Body:</Text>
<TextInput
placeholder="title"
style={[styles.titleInput, {height: 300}]}
onChangeText={setBody}
value={body}
/>
</View>
<Button
name="check-circle"
size={50}
color="black"
onPress={submit}
/>
</SafeAreaView>
)
}
const submit = async () => {
const diary = {title, body}
await AsyncStorage.setItem('diary',JSON.stringify(diary))
}
Change your submit function to this.
and it should work fine
const Home = ({navigation}) => {
const [refreshing, setRefreshing] = useState(false);
const [diary, setDiary] = useState(null)
whenRefresh = async () => {
setRefreshing(true);
const diary = await AsyncStorage.getItem('diary')
setDiary(JSON.parse('diary'))
setRefreshing(false)
}
return(
<View style={styles.home}>
<ScrollView
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={whenRefresh}/>}
style={styles.body}>
{diary ? <ListItem
title={diary.title}
subtitle="test"
onPress={()=>{console.log("listitem pressed")}}
/> : null}
I'm developing an app in React Native in which I'm trying to display some data provided by a fake API I set up using json server. I'm using the useContext hook to handle the general state of the app and since I'm fairly new to React Native and React in general I need some help handling the response I'm manipulating through the context API.
This is the State file I set up in the context folder
import React, { useReducer } from 'react'
import MenusReducer from './MenusReducer'
import MenusContext from './MenusContext'
import { baseUrl } from '../../shared/baseURL'
const MenusState = (props) => {
const initialState = {
menus: [],
selectedMenu: null
}
const [state, dispatch] = useReducer(MenusReducer, initialState)
const getMenus = async () => {
const response = await fetch(baseUrl + 'RESTAURANTES')
const data = await response.json()
console.log('This is the reducer working'); // This is a test log to see if it works
dispatch({
type: 'GET_MENUS',
payload: data
})
}
const getDetails = async (id) => {
const response = await fetch(`${baseUrl}RESTAURANTES/${id}`)
const data = await response.json()
dispatch({
type: 'GET_DETAILS',
payload: data
})
}
return (
<MenusContext.Provider value={{
menus: state.menus,
selectedMenu: state.selectedMenu,
getMenus,
getDetails
}}>
{props.children}
</MenusContext.Provider>
)
}
export default MenusState;
So here I set up a getMenus() function by which I get all the items I'd like to display in my components. As you can see, I put a test log inside the function to see if it works, which it does.
The problem comes when I try to get those items inside my app components. Here's one of the instances in which I try to get the items to display.
const Home = ({ navigation }) => {
const { menus, getMenus } = useContext(MenusContext)
const [search, setSearch] = useState('')
const [response, setResponse] = useState([])
const [categories, setCategories] = useState(allCategories)
const [loading, setLoading] = useState(true)
useEffect(() => {
const data = async () => await getMenus();
console.log('This is the app executing');
setLoading(false);
setResponse(data)
console.log(response);
}, [])
// ... some code later
return (
<ScrollView style={styles.yScroll}>
<View>
<Text style={styles.sectionTitle}>Destacados</Text>
</View>
<ScrollView horizontal showsHorizontalScrollIndicator={false}>
<View style={styles.sectionContainer}>
<Text>{response[0]}</Text> // Here's where I'm trying to print something about the response but it's not working
</View>
</ScrollView>
<View>
<Text style={styles.sectionTitle}>Categorias</Text>
</View>
<View style={styles.sectionContainer}>
{categories.map((item, index) => {
return (
<View key={index} style={styles.category}>
<Text>{item}</Text>
</View>
)
})}
</View>
</ScrollView>
)
}
So inside one of the ScrollViews I'm setting up a test to see if the response can be displayed, which it is not. However, inside the useEffect, I'm setting up a test log with the message 'This is the app executing' which is working, BUT, the response being logged is an empty array.
I'm sure the problem I'm facing has something to do with the asynchronous response between app and server, but I have no clear idea as to how I can address this.
Can someone please point me in the right direction? Thanks in advance!!
Based on your code, I think you can do this
const Home = ({ navigation }) => {
const { menus, getMenus } = useContext(MenusContext)
const [search, setSearch] = useState('')
const [categories, setCategories] = useState(allCategories)
const [loading, setLoading] = useState(true)
useEffect(() => {
const data = async () => await getMenus();
console.log('This is the app executing');
data();
setLoading(false);
}, [])
// ... some code later
return (
<ScrollView style={styles.yScroll}>
<View>
<Text style={styles.sectionTitle}>Destacados</Text>
</View>
<ScrollView horizontal showsHorizontalScrollIndicator={false}>
<View style={styles.sectionContainer}>
<Text>{menus[0]}</Text> // Here's where I'm trying to print something about the response but it's not working
</View>
</ScrollView>
<View>
<Text style={styles.sectionTitle}>Categorias</Text>
</View>
<View style={styles.sectionContainer}>
{categories.map((item, index) => {
return (
<View key={index} style={styles.category}>
<Text>{item}</Text>
</View>
)
})}
</View>
</ScrollView>
)
}