Button not displaying fetch results to the component? - reactjs

I am creating a currency converter app and it will retrieve currency value from the API and multiply with the text input for the result. Both the API result and Text input are stored in State and passing as props to the Component
import React from 'react';
import { StyleSheet, Text, View,TextInput,Button } from 'react-native';
import DisplayResult from './src/DisplayResult'
export default class App extends React.Component {
state = {
currency:'',
pokeList: '',
}
placeNameChangeHandler=(val)=>{
this.setState({currency:val});
}
// console.log(this.state.currency);
async findCurrency () {
try {
//Assign the promise unresolved first then get the data using the json method.
const pokemonApiCall = await fetch('https://free.currconv.com/api/v7/convert?q=KWD_INR&compact=ultra&apiKey={my_api_Key}');
const pokemon = await pokemonApiCall.json();
this.setState({pokeList: pokemon['KWD_INR']});
// console.log(pokemon);
} catch(err) {
console.log("Error fetching data-----------", err);
};
<DisplayResult convert={this.state.pokeList} result={this.state.currency} />
}
render() {
return (
<View style={styles.container}>
<TextInput
placeholder="Currency"
value = {this.state.currency}
onChangeText={this.placeNameChangeHandler}
/>
<Button
title="Search"
onPress={this.findCurrency.bind(this)}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
DisplayResult
const DisplayResult =(props)=>{
const {convert,result} = props
console.log(convert);
return (
<View>
<Text>{result*convert}</Text>
</View>
)
}
export default DisplayResult;
I am trying to pass the API result and text input to the display component and this will multiply the values and will give the result.
Now this is not functioning or giving result
why this is not showing and where it's going wrong?

In your findCurrency method you just "call" the DisplayResult without returning it, but I don't think this is the good method to display your result.
You can use your component directly within the render method by testing your state variables, like this :
findCurrency = async () => {
try {
const pokemonApiCall = await fetch(
"https://free.currconv.com/api/v7/convert?q=KWD_INR&compact=ultra&apiKey={my_api_Key}"
);
const pokemon = await pokemonApiCall.json();
this.setState({ pokeList: pokemon["KWD_INR"] }); // You set your "pokeList" variable up
} catch (err) {
console.log("Error fetching data-----------", err);
}
}
Note that you remove the DisplayResult call here and the function became an arrowed function, then in your render method use the test to make your result appear only if pokeList isn't empty :
render() {
return (
<View style={styles.container}>
<TextInput
placeholder="Currency"
value={this.state.currency}
onChangeText={this.placeNameChangeHandler}
/>
<Button title="Search" onPress={this.findCurrency.bind(this)} />
{this.state.pokeList !== "" && (
<DisplayResult
convert={this.state.pokeList}
result={this.state.currency}
/>
)}
</View>
);
}
Then, you don't have to bind your function in the onPress method like this, JavaScript immediately calls the function if you do this, instead, use arrow functions, you can access this by doing so in your function AND the onPress method doesn't call it if you don't click on the button, you just have to specify which function to execute when clicked :
<Button title="Search" onPress={this.findCurrency} />
If you have parameters in your function, use an arrow function instead :
<Button title="Search" onPress={() => yourFunction(param)} />
This should do the trick.

Try writing your function like that :
const findCurrency = async() => {
// ...
};
and call it like that
<Button
title="Search"
onPress={() => this.findCurrency()}
/>
I personnaly never use .bind because I think this is very unclear.

try using conditional rendering,
if data fetched, then only render.
import React from 'react';
import { StyleSheet, Text, View,TextInput,Button } from 'react-native';
import DisplayResult from './src/DisplayResult'
export default class App extends React.Component {
state = {
currency: '',
pokeList: '',
}
placeNameChangeHandler=(val)=>{
this.setState({currency:val});
}
// console.log(this.state.currency);
this.findCurrency.bind(this);
async findCurrency () {
try {
//Assign the promise unresolved first then get the data using the json method.
const pokemonApiCall = await fetch('https://free.currconv.com/api/v7/convert?q=KWD_INR&compact=ultra&apiKey={my_api_Key}');
const pokemon = await pokemonApiCall.json();
this.setState({pokeList: pokemon['KWD_INR']});
// console.log(pokemon);
} catch(err) {
console.log("Error fetching data-----------", err);
};
}
render() {
return (
<View style={styles.container}>
<TextInput
placeholder="Currency"
value = {this.state.currency}
onChangeText={this.placeNameChangeHandler}
/>
<Button
title="Search"
onPress={this.findCurrency()}
/>
</View>
{
if(this.state.pokeList !== '' || this.state.currency !== '') ?
<DisplayResult convert={this.state.pokeList} result={this.state.currency} /> : <div></div>
}
);
}
}

Related

how to change text from a function when clicking on the dice - React Native

How do I change my <Text style={styles.advice}> with API data from the function getUrl when I click on the dice?
Can it be because when I console log getUrl function i get a promise object. If that is the case how do I solve that?
Any advice if I could write this code any better would be appreciated!
Boxes.js
export function button() {
async function getUrl() {
const url = "https://api.adviceslip.com/advice";
const response = await fetch(url);
const data = await response.json();
const text = data.slip.advice;
return text;
}
getUrl();
}
export default class Boxes extends react.Component {
render() {
return (
<View style={styles.box}>
<Text style={styles.header}>{button.getUrl}</Text>
<Text style={styles.advice}>
It is easy to sit up and take notice, what's difficult is getting uu
and taking action
</Text>
<PatternDivider />
<Dice />
</View>
);
}
}
Dice.js
import { button } from "./Boxes";
export default class Dice extends react.Component {
render() {
return (
<Pressable style={styles.circle} onPress={button}>
<Image
source={require("../assets/icon-dice.png")}
style={styles.dice}
/>
</Pressable>
);
}
}
For this you'll want to use a state variable to store whatever text is returned from the URL. Your Boxes.js would look something like this:
import {useState} from 'react';
const [apiText, setApiText] = useState("It is easy to sit up and take notice, what's difficult is getting up and taking action");
export function button() {
async function getUrl() {
const url = "https://api.adviceslip.com/advice";
const response = await fetch(url);
const data = await response.json();
const text = data.slip.advice;
setApiText(text);
}
getUrl();
}
export default class Boxes extends react.Component {
render() {
return (
<View style={styles.box}>
<Text style={styles.header}>{button.getUrl}</Text>
<Text style={styles.advice}>
{apiText}
</Text>
<PatternDivider />
<Dice />
</View>
);
}
}
Your
<Text style={styles.advice}>
would initially be the text "It is easy to sit up and..." but on button press its state would be updated to the text returned from fetch(url). You can find out more about state variables here: https://reactnative.dev/docs/state

Firebase | Reading data from Firebase returns "undefined", but console.log shows precise result

EDIT: I'm new to Stack, but why was my earlier thread locked? It just said "similar thread is found somewhere else" and the thread wasn't at all similar.
I am currently trying to update my own little personal weight tracker using Firebase and React Native. However, whenever I log DataSnapshot.val() I receive the input 22, which is perfect. But when I return the very same value I receive Undefined.
I tried both get() and onValue() with the same results. The path is correct, since I get the correct data using console.log.
https://firebase.google.com/docs/database/web/read-and-write?authuser=2
I tried following the above documentation. But is it updated? snapshot is currently DataSnapshot?
Firebase:
const readWeight = (database, userId) => {
get(ref(database, `users/${userId}/weight`)).then((DataSnapshot) => {
try {
if (DataSnapshot.exists()) {
console.log(
`Weight found. Current weight is: ${DataSnapshot.val()} kg`
);
return DataSnapshot.val();
} else {
console.log("Weight wasn't found");
}
} catch (error) {
console.log(error);
}
});
};
HomeScreen.js
// Modules
import { react, useState, useEffect } from "react";
import { Text, TouchableOpacity, View, TextInput, Image } from "react-native";
import { LogOutButton } from "../Components/LogOutButton";
import { auth, writeUserData, database, readWeight } from "../firebase";
// Stylesheet
import { styles } from "../Stylesheet/Stylesheet";
export const HomeScreen = () => {
const user = auth.currentUser;
let currentUserWeight = readWeight(database, user.uid);
console.log("Current Weight: ", currentUserWeight);
return (
<View style={styles.profileMain}>
<View style={styles.profileInfoContainer}>
<View style={styles.HOME__profileWeightContainer}>
<Text style={styles.HOME__profileWeightText}>
Last Weight: {currentUserWeight}
</Text>
</View>
</View>
</View>
</View>
);
};
Data is loaded from Firebase asynchronously, and the return DataSnapshot.val() runs way after you actually call readWeight(database, user.uid). Adding some logging is probably the best way to see that flow.
The solution is to store the weight in the state of your component with the useState hook.
export const HomeScreen = () => {
const user = auth.currentUser;
// 👇
let { currentUserWeight, setCurrentUserWeight } = useState(0);
useEffect(() => {
const unsubscribe = get(ref(database, `users/${userId}/weight`)).then((DataSnapshot) => {
if (DataSnapshot.exists()) {
setCurrentUserWeight(DataSnapshot.val());
}
});
return () => unsubscribe();
, []}
// 👆
return (
<View style={styles.profileMain}>
<View style={styles.profileInfoContainer}>
<View style={styles.HOME__profileWeightContainer}>
<Text style={styles.HOME__profileWeightText}>
Last Weight: {currentUserWeight}
</Text>
</View>
</View>
</View>
</View>
);
};

Opening I frame in react-native

I'm integrating stripe payment in react-native and wants to open an iframe with specific url from after buttton click.
Scenario : user will enter the card details, details will be given to api end point and it will return an url that will contain the authentication part like OTP. So I want that url to be opened how to do so ? Let me if is there any better way to open that authentication middleware.
Adding code below
Payment.js
import React, { Component } from 'react';
import { View, Button } from 'react-native';
import stripe from 'tipsi-stripe';
import { doPayment } from '../api/api';
import { Auth } from './Auth';
stripe.setOptions({
publishableKey: 'pk_test_********',
});
export default class Payment extends Component {
state = {
isPaymentPending: false
}
requestPayment = () => {
this.setState({ isPaymentPending: true });
return stripe
.paymentRequestWithCardForm()
.then(stripeTokenInfo => {
return doPayment(10330, stripeTokenInfo.tokenId);
})
.then((res) => {
let url = "<iFrame src='" + res.intent_url + "' />"
console.log(res, url);
openAuthentication(url); --->>>> here i'm calling a function with url
})
.catch(error => {
console.warn('Payment failed', { error });
})
.finally(() => {
this.setState({ isPaymentPending: false });
});
};
render() {
return (
<View style={styles.container}>
<Button
title="Make a payment"
onPress={this.requestPayment}
disabled={this.state.isPaymentPending}
/>
</View>
);
}
}
openAuthentication = (url) => {
console.log("Here with props :::::::", url);
// here I want to open an iframe, i'm getting correct url, I've checked it in a static html page and it is working
<Auth url={url} />
}
const styles = {
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
};
Auth.js
import React, { Component } from 'react'
import {
View, StyleSheet
} from 'react-native'
import { WebView } from 'react-native-webview'
export default class Auth extends Component {
constructor(props) {
super(props)
console.log(props, ">>>>>>>>>>>")
}
render() {
console.log("In Auth -----------");
return (
<View style={styles.container}>
<WebView
source={{ uri: 'myUrl' }}
/>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
}
})
Error:
React.createElement type is invalid, expected a string or a class/function.
Auth needs to be returned by a render function, otherwise nothing will show up.
So, you'll want something similar to this:
render() {
return (
<View style={styles.container}>
<Button
title="Make a payment"
onPress={this.requestPayment}
disabled={this.state.isPaymentPending}
/>
{this.state.url && (
<Auth url={this.state.url} />
)}
</View>
);
}
You only want to render auth when you have the url.
For this, you want to update your state to look something like this:
state = {
isPaymentPending: false,
url: undefined
}
And
.then((res) => {
let url = "<iFrame src='" + res.intent_url + "' />";
this.setState({ url });
})
In order to update you state with the received url when the promise resolves. This will update your state, set the url, and re-render. Because you have an url, Auth should be rendered as well.
LE:
Your Auth.js should look something like this in order to be able to display static HTML. this.props.url should be valid HTML.
render() {
console.log("In Auth -----------");
return (
<View style={styles.container}>
<WebView
source={{ html: this.props.url }}
originWhitelist={['*']}
/>
</View>
);
}

React Native App Wont display Api results

Hello i'm trying to display the ai of these movies but the wont show on my phone but when i console log they work in my terminal, just having issues displaying the results to the device.
At first i wast giving a error
Uncaught TypeError:state.results.map is not an object
here is the code below
import React ,{ useState }from 'react';
import axios from'axios';
import { StyleSheet, Text, View ,ScrollView,TextInput} from 'react-native';
export default function App() {
const apiurl = "http://www.omdbapi.com/?i=tt3896198&apikey=?????";
const [state,setState] = useState({
s:'Enter A Movie...',
results:[],
selected:{}
});
const search = () => {
axios(apiurl + "&s=" + state.s).then(({data})=>{
let results = data.Search
console.log(results)
setState(prevState =>{
return {...prevState, results: results}
})
})
}
return (
<View style={styles.container}>
<Text style={styles.Header}>Movie DataBase</Text>
<TextInput
style={styles.searchBar}
onChangeText={text => setState(prevState =>{
return{...prevState, s: text}
})}
onSubmitEditing={search}
value = {state.s}
/>
<ScrollView style={styles.results}>
{state.results.map(result => {
<View key={result.imdbID} style={styles.result}>
<Text>{result.Title}</Text>
</View>
})}
</ScrollView>
</View>
);
}
You can directly set your results to your state like
setState( { ...state , results : results } )

React Component Props are receiving late. (Meteor JS)

I am working on a react-native and meteor js project.
My problem is that the props received from withTracker() function are only received in componentDidUpdate(prevProps) I don't get them in constructor or componentWillMount.
Another issue is when i pass props directly from parent to child. it receives them late due to which my component does not update
iconGroups prop comes from withTracker() method
and openSection props which i am using in this showGroupIcons()
is passed directly from parent to this component.
I want to open Accordian section that is passed to it via parent. but problem is in componentDidUpdate(prevProps) I am changing state due to which component re-renders.
openSection variable by default value is Zero. when props arrvies it value changes which i required But Accordian does not update.
Below is my code
import React, { Component } from 'react';
import Meteor, { withTracker } from 'react-native-meteor';
import {
View, Image, ScrollView, TouchableOpacity,
} from 'react-native';
import PopupDialog from 'react-native-popup-dialog';
import {Text, Icon, Input, Item, List,} from 'native-base';
import Accordion from 'react-native-collapsible/Accordion';
import { Col, Row, Grid } from 'react-native-easy-grid';
import styles from './styles';
import CONFIG from '../../config/constant';
import {MO} from "../../index";
const staticUrl = '../../assets/img/icons/';
class IconPickerComponent extends Component {
constructor(props) {
super(props);
this.state = {
dataSource: [],
itemName: 'apple1',
activeSections: 0,
showAccordian: true,
accordianData: []
};
}
componentDidUpdate(prevProps) {
if(prevProps.iconGroups !== this.props.iconGroups) {
let images = this.props.iconGroups.map(icon => icon.images);
let flatten = [].concat.apply([], images).map(img => { return {name: img, icon: CONFIG.ICON_URL+img+'.png'} })
this.setState({ filteredItems: flatten, dataSource: flatten, accordianData: this.props.iconGroups });
}
}
componentDidMount() {
this.props.onRef(this);
}
componentWillUnmount() {
this.props.onRef(null);
}
method() {
// this.setState(...this.state,{
// searchText: ''
// })
this.iconPicker.show(); // show icon picker
}
onSearchChange(text) {
this.setState({
showAccordian: !(text.length > 0)
});
const searchText = text.toLowerCase();
const filteredItems = this.state.dataSource.filter((item) => {
const itemText = item.name.toLowerCase();
return itemText.indexOf(searchText) !== -1;
});
this.setState({ filteredItems });
}
onIconSelect(item) {
this.setState({
itemName: item,
});
this.iconPicker.dismiss();
if (this.props.onIconChanged) {
this.props.onIconChanged(item);
}
}
_renderSectionTitle = section => {
return (
<View style={styles.content}>
<Text></Text>
</View>
);
};
_renderHeader = section => {
return (
<View style={styles.accordHeader}>
<Text style={{color: 'white'}}>{this.state.showAccordian} - {section.group}</Text>
<Text>
<Icon style={styles.downArrow} name="ios-arrow-down" />
</Text>
</View>
);
};
_renderContent = section => {
return (
<View style={styles.accordContent}>
{
section.images.map((img, key) => (
<TouchableOpacity onPress={() => this.onIconSelect(img)} key={key}>
<View style={styles.iconsGrid}>
<Image style={styles.image} source={{uri: CONFIG.ICON_URL+ img + '.png'}}/>
</View>
</TouchableOpacity>
))
}
</View>
);
};
_updateSections = activeSections => {
this.setState({ activeSections });
};
hasGroupIcons() {
return this.props.iconGroups.length > 0;
};
showGroupIcons() {
if(this.state.showAccordian){
let openSection;
if(!!this.props.openSection) {
let groupIndex = this.state.accordianData.findIndex(icon => icon.group === this.props.openSection);
if(groupIndex !== -1) {
openSection = groupIndex;
} else {
openSection = 0;
}
} else {
openSection = 0;
}
return(<Accordion
sections={this.state.accordianData}
activeSections={this.state.activeSections}
renderSectionTitle={this._renderSectionTitle}
renderHeader={this._renderHeader}
renderContent={this._renderContent}
onChange={this._updateSections}
initiallyActiveSection={openSection} />);
} else {
return(<View style={{flexWrap: 'wrap', flexDirection: 'row'}}>
{
this.state.filteredItems.map((item, key) => (
<TouchableOpacity onPress={() => this.onIconSelect(item.name)} key={key}>
<View style={styles.iconsGrid}>
<Image style={styles.image} source={{uri: item.icon}}/>
</View>
</TouchableOpacity>
))
}
</View>)
}
};
render() {
return (
<PopupDialog
overlayOpacity={0.8}
overlayBackgroundColor="#414141"
dialogStyle={styles.dialogBox}
containerStyle={styles.dialogContainer}
ref={(popupDialog) => { this.iconPicker = popupDialog; }}
>
<ScrollView>
<View style={styles.dialogInner}>
<Item searchBar rounded style={styles.searchbar}>
<Icon style={styles.searchIcon} name="search" />
<Input onChangeText={this.onSearchChange.bind(this)} style={styles.inputSearch} placeholder="Search" />
</Item>
{
this.hasGroupIcons() && this.showGroupIcons()
}
</View>
</ScrollView>
</PopupDialog>
);
}
}
export default withTracker(params => {
MO.subscribe('ipSubsId3', 'IconGroups');
return {
iconGroups: MO.collection('IconGroups', 'ipSubsId3').find({}),
};
})(IconPickerComponent);
I am new to react. I am assuming when props change component re-renders.
Use this life cycle method
static getDerivedStateFromProps(prevProps, prevState) {
if(prevProps.iconGroups !== this.props.iconGroups) {
let images = this.props.iconGroups.map(icon => icon.images);
let flatten = [].concat.apply([], images).map(img => { return {name: img, icon: CONFIG.ICON_URL+img+'.png'} })
this.setState({ filteredItems: flatten, dataSource: flatten, accordianData: this.props.iconGroups });
}
}
getDerivedStateFromProps is invoked right before calling the render method, both on the initial mount and on subsequent updates. It should return an object to update the state, or null to update nothing.
Read more about this lifecycle method here
I have fixed this issue. Actually my concepts were not right. I thought props are first received in constructor and componentWillMount. But I get all props in render() and everything works fine i dont have to use any lifecycle method to use props now

Resources