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