I am developing a simple weather app using react-native. Using fetch-api, i am requesting for weather of cities based on thier IDs but even though i am inputting different city IDs, i am getting the same default result . Here is my code :-
This is my main component
import React, {Component} from 'react';
import {StyleSheet, Text, View, TextInput} from 'react-native';
import Forecast from "./Forecast";
import OpenWeatherMap from "./openweather"
export default class Weather extends Component {
state = {
forecast: null
}
handleTextChange = event => {
let zip = event.nativeEvent.text;
OpenWeatherMap.fetchForecast(zip).then(forecast => {
console.log(forecast);
this.setState({forecast: forecast});
});
}
render() {
let content = null;
if(this.state.forecast !== null){
content = (
<Forecast
main = {this.state.forecast.main}
description = {this.state.forecast.description}
temp = {this.state.forecast.temp} />
);
}
return (
<View style={styles.container}>
<Text style={styles.welcome}>
Enter your City ID
</Text>
{content}
<TextInput
style={styles.input}
onSubmitEditing={this.handleTextChange} />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#808000',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
input:{
fontSize: 20,
borderWidth: 2,
padding: 2,
height: 40,
width: 100,
textAlign: "center"
}
});
This is my child component used only for display purposes..
Forecast.js
import React, {Component} from 'react';
import {StyleSheet, Text, View} from 'react-native';
class Forecast extends Component {
render(){
return(
<View style={styles.container}>
<Text style={styles.bigText}>
{this.props.main}
</Text>
<Text style= {styles.mainText}>
Current conditions: {this.props.description}
</Text>
<Text style= {styles.bigText}>
{this.props.temp} ^ F
</Text>
</View>
);
}
}
const styles= StyleSheet.create({
container: {height: 130},
bigText:{
flex:2,
fontSize: 20,
textAlign: "center",
margin: 10,
color: "#800000"
},
mainText : {flex:1, fontSize: 16, textAlign: "center", color: "#800000"}
});
export default Forecast;
This is where i use fetch api for requesting weather from openweathermap :-
const weather_api_key = "7e901f7ecb8b36c60ccca4a64c90ee1a";
const weather_url ="https://samples.openweathermap.org/data/2.5/weather?";
function zipurl(zip){
return `${weather_url}id=${zip}&APPID=${weather_api_key}`;
}
function fetchForecast(zip){
return fetch(zipurl(zip))
.then(response => response.json())
.then(responseJSON => {
return {
main:responseJSON.weather[0].main,
description:responseJSON.weather[0].description,
temp:responseJSON.main.temp
};
})
.catch(error=>{
console.error(error);
});
}
export default {fetchForecast: fetchForecast}
My path for different files is :-
You're using the samples.openweathermap.org endpoint, instead of the api.openweathermap.org endpoint. The second one is the one that you want to use, and that actually gives you different data depending on the query parameters.
By the way, thank you for posting the relevant code. It made it much easier to debug your problem. Although in my opinion, you posted much more than needed. I only used the last code snippet as that is where the problem lies. Not in the rendering of the data. Also for next time, consider adding a sample output of the API vs. what you were expecting. And I'd recommend you to change your API key now that you've got your solution.
Related
Im new in React, and Im starting with React native.
I'm working in my project, and in order to re-use code, I'm reading about HOC.
My use case is: I have a lot of views with a footer that have some buttons (one or two, it depends. They might have different actions, some of them navigates to another activity, other execute functions or state updates).
Im trying to execute a navigation.navigate from the "main" view, but I got an error: "Cant find variable: navigation".
This is my code:
index.js
import {
Text,
StyleSheet,
View,
TouchableOpacity,
ScrollView
} from 'react-native';
import withFooter from '../../components/withFooter';
const SignUp = ({ navigation }) => {
return (
<View style={styles.container}>
<View style={{ flex: 3 }}>
<Text>Test</Text>
</View>
</View>
)
};
export default withFooter(SignUp, {
buttons: [
{
text: 'Exit',
action: () => console.log('Exit'),
},
{
text: 'Accept',
action: () => navigation.navigate('PersonalDataSignUp'),
}
]
});
withFooter.js
import { Text, View, TouchableOpacity, StyleSheet } from 'react-native';
const withFooter = (WrappedComponent, { buttons }) => {
const WithFooter = props => {
return (
<>
<WrappedComponent {...props} />
<View style={{
flexDirection: 'row',
padding: 20
}}>
{
buttons.map(button => (
<TouchableOpacity style={[styles.button]} onPress={button.action}>
<Text style={{ fontWeight: '900' }}>{button.text}</Text>
</TouchableOpacity>
))
}
</View>
</>
)
};
return WithFooter;
};
const styles = StyleSheet.create({
button: {
flex: 1,
height: 50,
borderRadius: 5,
backgroundColor: '#FCCC00',
justifyContent: 'center',
alignItems: 'center',
borderColor: 'black',
borderWidth: 1
},
})
export default withFooter;
How can I send different functions from index in order to re use the footer code? Is there any other way to do it?. Thanks in advance!
react-native-multiselect-view is a seemingly simple library.....however I cant find a way to render a container as "pressed" (i.e. active) without physically pressing it. I want user to make choices and store them in Firebase.....this way when they re-open the app I will fetch choices from Firebase and update UI for their previous selections. Im guessing I need to change the data array data={['item1', 'item2']} to reflect whether item is active or not.....but cant figure out exactly how
import MultiSelectView from 'react-native-multiselect-view';
<MultiSelectView
data={['item1', 'item2']}
inactiveContainerStyle={{padding:2, borderColor: colorConsts.c3, opacity:0.25, borderWidth: 2, borderRadius:sizeConsts.widgetBorderRadius, backgroundColor:'white'}}
activeContainerStyle={{padding:2, borderColor: colorConsts.c2, borderWidth: 2, borderRadius:sizeConsts.widgetBorderRadius, backgroundColor:'white'}}
activeTextStyle={{color:colorConsts.c2}}
inactiveTextStyle={{color:colorConsts.c3}}
inactiveIcon={<IconFA name="plus-circle" size={20} style={{color:colorConsts.c3}}/>}
activeIcon={<IconFA name="minus-circle" size={20} style={{color:colorConsts.c2}}/>}
onSelectionStatusChange={this._onSelectionStatusChange}
/>
I emailed author who kindly came right back to me with: "It's not implemented at the moment. But here the code screenshot where it can be handled. U can raise a PR or fork repo and do it. If u need me to do it for u let me know.."
I decided instead to write my own and use much of the authors code (below)....I pull user preferences from Firebase....into Redux....into local state
/* eslint-disable react-native/no-inline-styles */
import React, {Component} from 'react';
import {View, TouchableOpacity, Text, StyleSheet} from 'react-native';
import {sizeConsts, colorConsts} from '../globalConst';
import IconFA from 'react-native-vector-icons/FontAwesome';
import database from '#react-native-firebase/database';
import {connect} from 'react-redux';
//*********************************************************
//REDUX
//*********************************************************
const passStateToProps = state => ({
arrObjInterests: state.profile.mySelf.arrInterests,
userId: state.auth.userId,
});
class MyMultiSelectView extends Component {
//To update the status of any arrInterests object, run a map on the array and check for some unique value of each object,
//in case of condition=true, return the new object with updated value, else same object.
_onPress = (label, oldValue) => {
//FIRST UPDATE STATE FOR QUICK UI RESPONSE
this.setState(
prevState => ({
//map through prevState interests array and return each object same as you saw it
//.....unless its a match in which case you update the value
arrInterests: prevState.arrInterests.map(obj =>
obj.label === label ? {...obj, value: !oldValue} : obj,
),
}),
() => {
//NEXT UPDATE FBS WITH UPDATED STATE
//prettier-ignore
database().ref('/profiles/users/' + this.props.userId + '/arrInterests').set(this.state.arrInterests);
},
);
};
constructor(props) {
super(props);
//populate state with interests array arrInterests from Redux arrObjInterests
this.state = {
arrInterests: this.props.arrObjInterests,
};
}
render() {
return (
//prettier-ignore
<View
style={{flexDirection: 'row', flexWrap: 'wrap', flex: 1, padding: 15}}>
{/* map through interests array and create TouchableOpacity for each */}
{this.state.arrInterests.map((item, index) => (
<TouchableOpacity key={index} onPress={() => {
this._onPress(item.label, item.value);
}}>
<View style={!item.value ? [styles.container, {borderColor: colorConsts.c3, opacity:0.25}] : [styles.container, {borderColor: colorConsts.c2}]}>
<Text style={!item.value ? [styles.text, {color:colorConsts.c3}] : [styles.text, {color:colorConsts.c2}]}>{item.label}</Text>
{!item.value ? <IconFA name="plus-circle" size={20} style={[styles.icon, {color:colorConsts.c3}]}/> : <IconFA name="minus-circle" size={20} style={[styles.icon, {color:colorConsts.c2}]}/>}
</View>
</TouchableOpacity>
))}
</View>
);
}
}
const styles = StyleSheet.create({
container: {
paddingVertical: 5,
marginTop: 10,
marginRight: 10,
paddingLeft: 10,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
borderWidth: 2,
borderRadius: sizeConsts.widgetBorderRadius,
backgroundColor: '#f8f8f8',
},
text: {
fontSize: 16,
color: 'black',
marginRight: 6,
},
icon: {
width: 20,
marginRight: 2,
},
});
export default connect(
passStateToProps,
null,
)(MyMultiSelectView);
I am using Expo to build a react native app with AWS for the backend.
I am trying to display a list of friends using FlatList and the AWS data. The list works and is visible on my web browser, but for some reason, it is not displaying on my Android phone. What might the issue be?
FriendsList.tsx
import { API, graphqlOperation } from 'aws-amplify';
import * as React from 'react';
import {useEffect, useState} from 'react';
import { FlatList, View, ScrollView, Text, StyleSheet } from 'react-native';
import { listUsers } from '../graphql/queries';
import FriendsListItem from '../components/FriendsListItem';
export default function FriendsList() {
const [ users, setUsers ] = useState([]);
useEffect( () => {
const fetchUsers = async () => {
try {
const usersData = await API.graphql(
graphqlOperation(
listUsers
)
)
setUsers(usersData.data.listUsers.items);
} catch (e) {
}
}
fetchUsers();
},[])
return (
<View style={ styles.container }>
<FlatList
style={{ width: '100%' }}
data={users}
renderItem={({ item }) => <FriendsListItem user={item} />}
keyExtractor={(item) => item.id}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
});
FriendsListItem.tsx
import * as React from 'react';
import { View, Text, StyleSheet, Image, TouchableWithoutFeedback } from 'react-native';
import { API, graphqlOperation, Auth } from "aws-amplify";
import { User } from './types';
export type FriendsListItemProps = {
user: User;
}
const FriendsListItem = ( props: FriendsListItemProps ) => {
const { user } = props;
return (
<TouchableWithoutFeedback>
<View style={styles.container}>
<View style={styles.lefContainer}>
<Image source={{ uri: user.imageUri }} style={styles.avatar}/>
<View style={styles.midContainer}>
<Text style={styles.username}>{user.name}</Text>
<Text numberOfLines={2} style={styles.status}>{user.email}</Text>
</View>
</View>
</View>
</TouchableWithoutFeedback>
);
}
export default FriendsListItem;
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
width: "100%",
justifyContent: 'space-between',
//height: '100%',
},
lefContainer: {
flexDirection: 'row',
padding: 16,
},
midContainer: {
justifyContent: 'space-around'
},
avatar: {
width: 60,
height: 60,
borderRadius: 50,
marginRight: 15,
},
username: {
fontWeight: 'bold',
fontSize: 16,
},
status: {
fontSize: 16,
color: 'grey',
},
});
Looking at the code example, in your FriendsListItem component, any time you use your "user" prop, you need to change it. For example, change this:
user.imageUri
to this:
user.item.imageUri
What you are passing in is an object (e.g. user), which then contains another object (e.g. item), which finally contains your data (e.g. imageUri).
I figured it out. Turns out I am just an idiot. The expo app on my phone was logged into a different account so that's why it wasn't showing any friends for that user.
I'm New to react native,
I'm Building a sample app and when entering data to TextInput field I'm Getting the warning.
I tried running it on Android emulator and on my Pocophone f1 device and got the same results.
I'm using VS Code as my IDE.
I'm Developing on Ubuntu 18.04
Can anyone help?
These are screenshots of the app
the data I'm entering.
The Warning I get
This is my Code
/**
* Sample React Native App
* https://github.com/facebook/react-native
*
* #format
* #flow
*/
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View, TextInput, Button} from 'react-native';
//type Props = {};
export default class App extends Component {
state = {
placeName: "",
places: []
}
placeNameChangedHandler = (event) => {
this.setState({
placeName: event
});
}
placeNameSubmitHandler = () => {
if (this.state.placeName === ""){
return;
}
this.setState(prevState => {
return {
places: prevState.places.concat(prevState.placeName)
};
});
};
render() {
const placesOutput = this.state.places.map(place => (
<Text>{place}</Text>
));
return (
<View style={styles.container}>
<View style={styles.inputContainer}>
<TextInput
style={{width: 200, borderColor: "black", borderWidth: 1}}
placeholder="Place Name"
value={this.state.placeName}
onChangeText={this.placeNameChangedHandler}
style={styles.PlaceInput} />
<Button title="Add" style={styles.placeButton} onPress={this.placeNameChangedHandler} />
</View>
<View>
{placesOutput}
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 26,
justifyContent: 'space-between',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
inputContainer:{
width: "100%",
flexDirection: "row",
justifyContent: "space-between",
alignItems: 'center'
},
PlaceInput:{
width: "70%"
},
placeButton:{
width: "30%"
}
});
I changed the code as suggested in the answer below and got an error as shown in the screen below
If you were to log "event" in your placeNameChangedHandler function you would see that it is an object not just the string value you're looking for. So you are setting your state to the full event object and then trying to render it on the screen.
You need to destructure the object to get the string value you're looking for.
placeNameChangedHandler = (event) => {
this.setState({
placeName: event.target.value
});
}
I found the issue: on the **onPress event handler I called the wrong function, sorry for the time I wested **
I'm new to react native, I am trying to get a menu composed of logos that someone could just scroll down then tap one to go into more detail about it.
So I have my App.js file like so:
import React from 'react';
import {
StyleSheet,
View,
Image,
ScrollView,
Text
} from 'react-native';
import getImageForRestaurant from './utils/getImageForRestaurant';
import Avatar from './components/Avatar';
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
restaurants: 'buffalo',
};
}
render() {
const {
restaurants
} = this.state;
return (
<View style={styles.appContainer}>
<View style={styles.titleContainer}>
<Text style={styles.title}>Title</Text>
</View>
<ScrollView style={styles.timerlist}>
<Avatar
initials="KC"
size={75}
source={getImageForRestaurant(restaurants)}
backgroundColor={'blue'}
onPressLinkImage={() => {
console.log('Pressed!');
}}
/>
</ScrollView>
</View>
);
}
}
const styles = StyleSheet.create({
appContainer: {
flex: 1,
justifyContent: 'center',
},
titleContainer: {
paddingTop: 35,
paddingBottom: 15,
borderBottomWidth: 1,
borderBottomColor: '#D6D7DA',
},
title: {
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
},
timerList: {
paddingBottom: 15,
},
container: {
flex: 1,
backgroundColor: '#34495E',
},
imageContainer: {
flex: 0,
},
image: {
flex: 1,
width: 75,
height: 75,
resizeMode: 'contain',
},
});
The getImageForRestaurant() method works as intended if I make it inside an <Image/> but if I try to make it the source of my "Avatar" component then it won't work.
My getImageForRestaurant.js file is just this:
const images = {
buffalo1: require('../assets/restaurants/logo1.jpeg'),
buffalo: require('../assets/restaurants/logo2.png'),
buffalo2: require('../assets/restaurants/logo3.jpeg'),
};
export default restaurants => images[restaurants];
And finally my Avatar.js is as follows:
import {
ColorPropType,
StyleSheet,
Text,
View,
Image,
TouchableOpacity
} from 'react-native';
import PropTypes from 'prop-types';
import React from 'react';
import getImageForRestaurant from '../utils/getImageForRestaurant';
export default function Avatar({
size,
backgroundColor,
initials,
source,
onPressLinkImage,
}) {
const style = {
width: size,
height: size,
borderRadius: size / 2,
backgroundColor,
};
return (
<View style={[styles.container, style]}>
<TouchableOpacity onPress={onPressLinkImage}>
<Text style={styles.text}>{initials}</Text>
<Image source={require(getImageForRestaurant(source))} />
{/*<Image source={require("../assets/restaurants/logo1.jpeg")} />*/}
</TouchableOpacity>
</View>
);
}
Avatar.propTypes = {
initials: PropTypes.string.isRequired,
size: PropTypes.number.isRequired,
source: PropTypes.number.isRequired,
backgroundColor: ColorPropType.isRequired,
onPressLinkImage: PropTypes.func.isRequired,
};
const styles = StyleSheet.create({
container: {
alignItems: 'center',
justifyContent: 'center',
},
text: {
color: 'white',
},
});
So if I just do an Image source, (the commented part) it works as a regular image, but then I need to hard-code the actual url and what I want is to just load all images one next to the other in a scrollable grid. Haven't been able to figure out how to do what I want. Could someone please point me in the right direction?
While Edison makes a good point about good practices, I believe your problem is that you are just requiring the image twice. The output of the require() is what you need to pass to the Image component. You are doing require of a require.
<Image source={require(getImageForRestaurant(source))} />
Probably just changing to this should work:
<Image source={getImageForRestaurant(source)} />
It’s a bad practice to generate url inside the source prop. Always make sure that the necessary URL is built before its passed inside source prop. You can use a variable to build your URL and then pass it to source prop. (In your case, image is imported inside helper function and hence I will use image variable)
const image = getImageforRestaurant(source)
<Image source={image} />
When you want to load images from the internet do it like this.
const link = ‘http://example.com/image.png’
<Image source={{uri: link}} />