Rendering Firebase data in map function - javascript

I'm trying to render data from a firebase get function but it isn't displaying anything. The images console.log displays 2 values but it doesn't get rendered on the page. Does anyone have suggestions why that is.
function cards(){
store.collection('users').get().then(snapshot => {
images = snapshot.docs.map(doc => doc.data().image)
console.log(images)
return images.map((doc) => {
return (
<Card style={[styles.card, styles.card1]}>
<Text style={styles.label}>A</Text>
</Card>
)
})
})
}
return (
<View>
<View style={styles.viewport}>
<CardStack style={styles.content}>
{cards()}
</CardStack>
</View>
</View>
)
}

You are trying to call a asynchrounous function and get a return from it by using a then. You will always get an undefined from it because the then finished when your function already returned undefined or in this case nothing.
Try it with using a state and handling the async call correctly like here:
import React, { useState, useEffect } from "react";
const YourComponent = () => {
const [list, setLits] = useState([]);
useEffect(() => {
const snapshot = await store.collection("users").get();
const images = [];
snapshot.docs.forEach((s) => {
images.push(doc.data().image);
});
setLits(images);
}, []);
return (
<View>
<View style={styles.viewport}>
<CardStack style={styles.content}>
{list.map((i) => {
return (
<Card style={[styles.card, styles.card1]}>
<Text style={styles.label}>A</Text>
</Card>
);
})}
</CardStack>
</View>
</View>
);
};

Related

React Native, Error: Objects are not valid as a React child

I'm passing down props to a different component so I can make an api call.
It makes the api call and i can console.log fine, but won't render the screen and I get this error:
"Error: Objects are not valid as a React child (found: object with keys {_U, _V, _W, _X}). If you meant to render a collection of children, use an array instead."
Code:
const BlogScreen = route => {
const blog_id = route.route.params.blog_id;
return (
<SafeAreaView style={styles.screen}>
<Header />
<BlogDetails blog_id={blog_id} />
</SafeAreaView>
);
};
export default BlogDetails = async props => {
const blog_id = props.blog_id;
console.log(blog_id);
await axios
.get(url)
.then(res => {
console.log(res.data);
});
return (
<View style={{width: '100%', flex: 1}}>
<Text></Text>
</View>
);
};
The BlogDetails component does not seem right.
Here's a solution:
export default BlogDetails = props => {
const blog_id = props.blog_id;
console.log(blog_id);
useEffect(() => {
axios
.get(url)
.then(res => {
console.log(res.data);
});
},[])
return (
<View style={{width: '100%', flex: 1}}>
<Text></Text>
</View>
);
};

Im not able to set an initial state in my react-native application

Im making a react-native app and I need to get some data (articles) from a GraphQL server and then, list the articles.
My problem is, when I run my app, the first time my HomeScreen component render, the state is empty and I cant see any of the articles because the response from the server takes some time to load.
I tried to use a condition where I check if the response has no errors and has finished loading, then I save the articles to the state son when I render my articleList, the state can have the articles, but it throws an error:
Too many re-renders. React limits the number of renders to prevent an infinite loop.
All I need is to have an initial state in my application before I render my home component
My code:
const HomeScreen = () => {
const {loading, error, data} = useQuery(queryRepository.GET_ARTICLES);
const [articles, setArticles] = useState([]);
const filterByCategory = name => {
setArticles(CategoryService.filterByCategoryName(name, data.items));
};
if (loading) {
return <Spinner />;
} else if (error) {
return <Error>{error}</Error>;
} else {
setArticles(data.items);
}
return (
<View style={globalStyles.container}>
<View>
<View style={globalStyles.categoryMenuContainer}>
<CategoryMenu filterByCategory={filterByCategory} />
</View>
<View style={globalStyles.reviewsContainer}>
<ArticleList articles={articles} />
</View>
</View>
</View>
);
};
Try useEffect for set state.
const HomeScreen = () => {
const { loading, error, data } = useQuery(queryRepository.GET_ARTICLES);
const [articles, setArticles] = useState([]);
const filterByCategory = name => {
setArticles(CategoryService.filterByCategoryName(name, data.items));
};
useEffect(() => {
if (!loading && !error && data) {
setArticles(data.items);
}
}, [data, loading, error]);
if (error) {
return <Error>{error}</Error>;
}
if (loading) {
return <Spinner />;
}
return (
<View style={globalStyles.container}>
<View>
<View style={globalStyles.categoryMenuContainer}>
<CategoryMenu filterByCategory={filterByCategory} />
</View>
<View style={globalStyles.reviewsContainer}>
<ArticleList articles={articles} />
</View>
</View>
</View>
)
};
Everytime you call setArticles, a new render will happen in your component, hence why you should not use setters within the render function itself, you should use hook useEffect for that

How to pass onPress to props.children?

I'm trying to make a wrapper component in react-native that I can pass down all its props to the children it wraps around. What I really want is to pass down all function props down to all its children. It looks something like this below. I want the onPress in Wrapper to be called when the TouchableOpacity is pressed.
I tried this below but it doesn't work
const Wrapper = ({children,...props})=>{
return <View {...props}>{children}</View>
}
const App = ()=>{
return (
<View style={{flex:1}}>
<Wrapper onPress={()=>{console.log(2)}}>
<TouchableOpacity/>
</Wrapper>
</View>
)
}
It looks like you're looking to map the children and apply the props to each one. That might look like this:
const Wrapper = ({children,...props})=>{
return (<>
{React.Children.map(children, child => (
React.cloneElement(child, {...props})
))}
</>);
}
(method of mapping the children borrowed from this answer)
const App = () => {
return (
<View style={{ flex: 1 }}>
<TouchableOpacity onPress={() => {
// do the action need here here
}}>
<Wrapper >
</Wrapper>
</TouchableOpacity>
</View>
)
}
I would advise you to use hooks function instead
If you try to reuse functions that are related
** useAdd.js **
export default () => {
const addFuction(a, b) => {
// do preprocessing here
return a + b
}
return [addFuction]
}
main componet
import useAdd from "/useAdd";
const App = () => {
const [addFuction] = useAdd()
return (
<View style={{ flex: 1 }}>
<TouchableOpacity onPress={() => {
addFuction(4,5)
}}>
...action component...
</TouchableOpacity>
</View>
)
}
console in useAdd hook.... to see visual changes use the react useState

Invalid Hook Call - React Hooks

I'm really new to JS and React. I get this error:
Invalid Hook Call
when I try to make a component appear and disappear when another component is clicked. This is my code:
const RenderList = ({data}) => {
return data.map((option, index) => {
return <Item title={option}/>
});
};
const Header = ({ title, style, press }) => (
<TouchableHighlight onPress={press}>
<Text style={style} >{title}</Text>
</TouchableHighlight>
)
const RenderItem = ( {item} ) => {
console.log(styles)
let dataToShow;
const [listState, setListState] = useState(true);
if (listState){
dataToShow = <RenderList data={item.data}/>
} else {
dataToShow = <Text/>
}
return (
<View style={styles.section}>
<Header title={item.title} style={styles.header} press={setListState(!listState)}/>
{dataToShow}
</View>
)}
EDIT
RenderItem is used in a flat list element as a function. (From what I understand)
const SettingsSection = (props) => {
const db = props.data;
return(
<View>
<FlatList
style={styles.sectionList}
data={db}
renderItem={RenderItem}
keyExtractor={item=>item.title}
ItemSeparatorComponent={FlatListItemSeparator}
/>
</View>
);
}
renderItem, as the name suggests, is a render prop, and as such is called directly (like so: renderItem({item})), not instantiated as a component (like so: <RenderItem item={item}/>).
This translates to React not creating the appropriate rendering "context" for hooks to work. You can make sure your RenderItem function is instantiated as a component by using it like this on the render prop:
<FlatList
style={styles.sectionList}
data={db}
renderItem={item => <RenderItem {...item}/>} // see here!
keyExtractor={item=>item.title}
ItemSeparatorComponent={FlatListItemSeparator}
/>
That way, RenderItem is treated as a component and thus can use hooks.
I think problem is occurring due to setListState(!listState) with press. I suggest you to wrap your state changing method into a function. Because onPress accepts only function type but you are giving it a return statement from hooks.
const RenderList = ({data}) => {
return data.map((option, index) => {
return <Item title={option}/>
});
};
const Header = ({ title, style, press }) => (
<TouchableHighlight onPress={press}>
<Text style={style} >{title}</Text>
</TouchableHighlight>
)
const RenderItem = ( {item} ) => {
console.log(styles)
let dataToShow;
const [listState, setListState] = useState(true);
if (listState){
dataToShow = <RenderList data={item.data}/>
} else {
dataToShow = <Text/>
}
return (
<View style={styles.section}>
<Header
title={item.title}
style={styles.header}
press={()=>{
setListState(!listState)
}}
/>
{dataToShow}
</View>
)}

How to get value from async function in react-native?

function CategoryCard (props) {
const [done, setDone] = React.useState(null);
let check;
React.useEffect(() => {
async function checkData() {
check = await getData(props.path);
// prints CORRECTLY
console.log(check);
}
checkData();
//prints INCORRECTLY
console.log(check);
setDone(true);
}, []);
return (
<View>
{done ? (
<Text>{check}</Text>
):(
<View style={[styles.container, styles.horizontal]}>
<ActivityIndicator size="large" color="#99ff99" />
</View>
)}
</View>
);
}
How can I get a value from an async function into my regular function in react-native?
In the above code, the first console.log prints the expected values, but the second one gives undefined, as if the async function never had any effect on the variable check.
I need the value of check from getData(props.path) in order to display it in the <Text> component.
Any ideas?
Put it in the state.
function CategoryCard (props) {
const [check, setCheck] = React.useState(null);
React.useEffect(() => {
async function checkData() {
const data = await getData(props.path);
setCheck(data);
}
checkData();
}, []);
return (
<View>
{check ? (
<Text>{check}</Text>
):(
<View style={[styles.container, styles.horizontal]}>
<ActivityIndicator size="large" color="#99ff99" />
</View>
)}
</View>
);
}

Categories