Objects are not valid as a React child, use an array instead - reactjs

I am trying to render the first and last name from a json request using axios.
I am getting the following error you see in the title. I have included a snack example here reproducing the error exactly as well as added the code below.
Thank you
const plsWork = () => {
// Make a request for a user with a given ID
return axios.get('https://randomuser.me/api')
.then(({data}) => {
console.log(data);
return data
})
.catch(err => {
console.error(err);
});
}
const userName = (userInfo) => {
const {name: {first, last}} = userInfo;
return {first}, {last};
}
export default function App() {
const [data, setData] = React.useState(' ')
const [userInfos, setUserInfos] = React.useState([]);
React.useEffect(() => {
plsWork().then(randomData => {
setData(JSON.stringify(randomData, null, 4) || 'No user data found.')
setUserInfos(randomData.results)
})
}, []);
return (
<View>
<ScrollView>
{
userInfos.map((userInfo, idx) => (
<Text key={idx}>
{userName(userInfo)}
</Text>
))
}
<Text style={{color: 'black', fontSize: 15}}>
{data}
</Text>
</ScrollView>
</View>
);
}

You have to return a React Component in the userName function.
In the line 21:
Change from return {first}, {last} to return <>{first}, {last}</>.
It should work!
Here is code edited: snack expo

Related

Render component from array values in React Native

I'm trying to render component/function from array values.
Main function
const GeneratedHistory = () => {
return (
<View style={styles.container}>
<View style={styles.headerWrapper}>
<Text variant="headlineLarge" style={styles.headersText}>Historia</Text>
<Text variant='labelMedium'>Generowane kody</Text>
</View>
<View style={styles.mainWrapper}>
<ScrollView>
{getItems()}
</ScrollView>
</View>
</View>
I retrieving values from Firestore and saves what i want to array named Items.
function getItems() {
const items = [];
try {
firebase.firestore().collection("Generated").where("username", "==", auth.currentUser.email)
.get().then((querySnapshot) => {
querySnapshot.forEach((doc) => {
items.push({
qrImage: doc.get("qrImage"),
qrText: doc.get("qrText"),
time: doc.get("time"),
})
});
items.map((item) => {
console.log(item.qrText)
})
});
} catch (error) {
alert('Error occured')
}
}
Nextly i map the array, printing to console and trying to render function named SingleElement.
function singleElement(text) {
return (
{text}
)
}
Logging to console work's fine, but i can't render the function.
Screen just stays white.
So, I have to use async function, in my case, I fetch the data when the window opens and save it to array.
useEffect(() => {
async function fetchData() {
todoRef
.onSnapshot(
querySnaphsot => {
const items = []
querySnaphsot.forEach((doc) => {
const { qrImage, qrText, time } = doc.data()
items.push({
id: doc.id,
qrImage,
qrText,
time,
})
setItems(items);
})
}
)
} fetchData()
}, [])
Then I map the elements and display them in the component.
items.map((item) => {
return <YourComponent key={item.id} text={item.qrText} time={item.time}>
</YourComponent>
})
}

Trying to add multiple images in an object

I have a form object that holds all my input values. I am working with the react-native camera component and what I am trying to accomplish is every time a picture is added, it is added to the object. With my current code, I am getting invalid attempts to spread non-iterable instances.
import { TicketContext } from "../store/TicketContext";
function Pictures() {
const [hasCameraPermissions, setHasCameraPermissions] = useState();
const [picture, setPicture] = useState();
const { form, setForm } = useContext(TicketContext);
//Add picture to form //
const handleAddPicture = () => {
setForm([...(form ?? []), { picture: picture.uri }]);
setPicture();
};
const cameraRef = useRef();
//Get Permission to use Camera//
const handleCameraPermissions = async () => {
const cameraPermissions = await Camera.requestCameraPermissionsAsync();
setHasCameraPermissions(cameraPermissions.status === "granted");
};
// Check for permissions on load //
useEffect(() => {
handleCameraPermissions();
}, []);
if (hasCameraPermissions === undefined) {
return <Text>Permissions Required...</Text>;
} else if (!hasCameraPermissions) {
return <Text>Camera Permission Denied. Please change in settings.</Text>;
}
//Take Picture //
const handleTakePicture = async () => {
const options = { base64: true, exif: false, quality: 1 };
const newPicture = await cameraRef.current.takePictureAsync(options);
setPicture(newPicture);
};
if (picture) {
return (
<SafeAreaView style={styles.container}>
<Image
style={styles.preview}
source={{ uri: "data:image/jpg;base64," + picture.base64 }}
/>
<View style={styles.buttonContainer}>
<Button title="Add Picture" onPress={handleAddPicture} />
</View>
</SafeAreaView>
);
}
return (
<SafeAreaView style={styles.container}>
<Camera style={styles.cameraContainer} ref={cameraRef}>
<View style={styles.buttonContainer}>
<Button title="Take Picture" onPress={handleTakePicture} />
</View>
</Camera>
</SafeAreaView>
);
}
export default Pictures;
setPicture returns the state back to undefined
my state in context
const [form, setForm] = useState({});
//[form ,setForm]
// make sure you object keys are camel case.
const handleAddPicture = () => {
setForm([...(form ?? []), { picture: picture.uri }]);
setPicture();
};

React Native WebSocket onmessage Firing After Re-rendering

I'm encountering a really weird issue with react native and websockets. I'm using a functional component, assigning the websocket to useRef, and there are a few other react hooks as well. When the view loads the websocket is loaded and works just fine. The messages come in just fine as well.
However when I call a hook unrelated to the websocket, the websocket's "onmessage" gets called.
const Home = ({ navigation }) => {
const store = useStore(); // from mobx store using React.useContext
const [riders, setRiders] = useState(store.schedule.riders);
const wss = useRef(null);
useEffect(() => {
const driverId = store.authentication.user.driver;
wss.current = new WebSocket(url);
wss.current.onopen = () => console.log('ws opened');
wss.current.onclose = (e) => console.log('ws closed', e.code, e.reason);
wss.current.onmessage = (e) => {
console.log('onmessage');
};
return () => {
wss.current.close();
};
}, []);
const handleStatusPress = () => {
// AFTER THIS EXECUTES "onmessage" GETS CALLED
store.schedule.updateStatus(ride.id, 'completed');
};
return (
<View>
{riders.map((s) => {
return (
<Animated.View
key={s.id}
style={{ ...styles.riderCard }}
>
<View style={styles.column}>
<Text style={styles.riderName}>{s.name}</Text>
<Text style={styles.riderType}>PICKUP</Text>
<Text style={styles.riderTime}>{moment(parseInt(s.datetime, 10)).format('h:mm A')}</Text>
</View>
<TouchableOpacity
style={styles.bubble}
onPress={() => {
handleStatusPress(s);
}}
>
<Text style={styles.riderStatus}>{s.status}</Text>
</TouchableOpacity>
</Animated.View>
);
})}
</View>
);
});

React Native Deck Swiper

I am trying to make a GET request to an enpoint using the two functional components below to display in my react native deck swipper
//using fetch
const getDataUsingFetch = () => {
fetch(latestNews+ApiKey)
.then((response) => response.json())
.then((responseJson) => {
// set the state of the output here
console.log(responseJson);
setLatestNews(responseJson);
})
.catch((error) => {
console.error(error);
});
}
//using anxios
//asynchronous get request call to fetech latest news
const getDataUsingAnxios = async () => {
//show loading
setLoading(true);
setTimeout(async () => {
//hide loading after the data has been fetched
setLoading(false);
try {
const response = await axios.get(latestNews+ApiKey);
setLatestNews(response.data);
setLoading(false);
console.log(getLatestNews);
} catch (error) {
// handle error
alert(error.message);
}
}, 5000);
};
Data returned when logged from console:
Array [
Object {
"category_id": "8",
"content": "Hi",
"created_at": "2020-11-12T12:43:03.000000Z",
"featured_image": "splash-background_1605184983.jpg",
"id": 19,
"news_url": "doerlife.com",
"title": "I m good how about you",
"updated_at": "2020-11-12T12:43:03.000000Z",
}....]
I now save the data into a state array
const [getLatestNews, setLatestNews] = useState([]);
Here is my swipper(some code ommited - not necessary)
<Swiper
ref={useSwiper}
//cards={categoryID(docs, "2")}
cards={getLatestNews}
cardIndex={0}
backgroundColor="transparent"
stackSize={2}
showSecondCard
cardHorizontalMargin={0}
animateCardOpacity
disableBottomSwipe
renderCard={(card) => <Card card={card} />}
.....
When I try to access any data in the array from my Card reusable component, e.g card.featured_image
I WILL GET THIS ERROR - TypeError: undefined is not an object (evaluating 'card.featured_image').
PLEASE CAN SOMEONE HELP ME.
//Card reusable component for deck swipper
import React from 'react'
import { View, Text, Image, ImageSourcePropType } from 'react-native'
import styles from './Card.styles'
const Card = ({ card }) => (
<View activeOpacity={1} style={styles.card}>
<Image
style={styles.image}
source={card.featured_image}
resizeMode="cover"
/>
<View style={styles.photoDescriptionContainer}>
<Text style={styles.title}>{`${card.title}`}</Text>
<Text style={styles.content}>{`${card.content}`}</Text>
<Text style={styles.details}>
Swipe Left to read news in details
</Text>
</View>
</View>
);
export default Card
I've done something similar to this before so I think I can help a bit. The problem here is that your getLatestNews state has not been updated yet before the cards render. You can fix the problem by having another state called "isDataReturned". Then, have a useEffect that triggers whenever getLatestNews's length changes. If getLatestNews's length is > 0, then you can set isDataReturned to be true and render the deck only when isDataReturned is true.
Here's a code sample that I made:
const [getLatestNews, setLatestNews] = useState([]);
const [dataIsReturned, setDataIsReturned] = useState(false)
useEffect(() => {
const fetchData = async () => {
const result = await axios(
'https://cat-fact.herokuapp.com/facts',
);
setLatestNews(result.data);
};
fetchData();
}, []);
useEffect(() => {
if (getLatestNews.length > 0) {
setDataIsReturned(true)
} else {
setDataIsReturned(false)
}
}, [getLatestNews.length])
if( dataIsReturned === true) {
return (
<View style={styles.container}>
<Swiper
cards={getLatestNews}
renderCard={(card) => {
return (
<View style={styles.card}>
<Text>{card.text}</Text>
</View>
)
}}
onSwiped={(cardIndex) => {console.log(cardIndex)}}
onSwipedAll={() => {console.log('onSwipedAll')}}
cardIndex={0}
backgroundColor={'#4FD0E9'}
stackSize= {3}>
</Swiper>
</View>)
} else {
return(<Text>Loading</Text>)
}
In the renderCard attribute, i changed it from
renderCard={(card) => <Cardz card={card} />}
to
renderCard={(card) => (card && <Cardz card={card} />) || null}
and it worked.

how to loop multiple arrays in react native

I am actually want to iterate a collection in firestore which contains multiple documents, and i want to return the doc that contain field uid equal of my uid.
that's the data in firestore:
const { user } = this.props ;
console.log("getting user data: ", user )
that's my code:
render() {
const auth = this.props.auth;
console.log("getting user id: ", auth.uid);
const userData = user.map((item)=>(
(item.uid) = (auth.uid)
? <Text color="white" size={28} style={{ paddingBottom: 8 }}>
{ item.displayName } </Text>
: <Text color="white" size={28} style={{ paddingBottom: 8 }}> Error
</Text>
)
);
return (
<Block style={styles.profileTexts}>
{userData}
</Block>
)
}
const mapStateToProps = ( state ) => {
console.log("state firebase",state);
return{
auth: state.firebase.auth,
user: state.firestore.ordered.users,
}
}
const mapDispatchToProps = (dispatch) => {
return {
signOut: () => dispatch(signOut()),
}
}
export default compose(
connect(mapStateToProps, mapDispatchToProps),
firestoreConnect([
{ collection: 'users'},
]))(Profile)
But i got this error:
"TypeError: undefined is not an object (evaluating 'o.map')"
inside your map you are using assignment operator instead of comparison operator.
if you just want to return an item which is same as auth.uid use a filter instead
Create a function which returns the item.uid === auth.uid
userFn = () => {
const {user} = this.props;
const authUser = user.filter(item => {
return item.uid === auth.uid
})
if (authUser.length>1){
return <Text>{authUser[0].displayName}</Text>
}
else return <Text>Error</Text>
}
then inside return statement
return (
<View style={{flex:1,justifyContent:'center'}}>
{this.userFn()}
</View>
);
You use user in render() method, but I don't see where did you get them?
I think this can fix the problem.
render() {
const auth = this.props.auth;
console.log("getting user id: ", auth.uid);
const user = this.props.user // <=== add this line
const userData = user.map((item)=>(
...

Resources