I am having trouble rendering data from an array React Native - javascript

i can log the data but cant seem to display it, the screen is completely blank.
im not getting any errors but still nothing on the screen
import React from 'react';
import { View, StyleSheet, Text } from 'react-native';
import firestore from '#react-native-firebase/firestore';
import { FlatList } from 'react-native-gesture-handler';
export default class StockAdjustmentScreen extends React.Component {
constructor(props) {
super(props)
this.state = {
stockAdjustments: [],
}
}
componentDidMount() {
firestore().collection('stockAdjustments')
.get().then((querySnapshot) => {
querySnapshot.forEach((doc) => {
this.state.stockAdjustments.push({
id: doc.id, ...doc.data()
})
})
console.log(this.state.stockAdjustments)
})
.catch((error) => {
console.log
(error)
})
}
render() {
let Adjustments = this.state.stockAdjustments.map((val, key) => {
return (
<View key={key}>
<FlatList style={styles.texts}>{val.comments}</FlatList>
</View>
)
})
return (
<View>
{Adjustments}
</View>
)
}
}
here is the data i am trying to display

You have to use:
this.setState({...})
instead of:
this.state.stockAdjustments.push({...})
in order to trigger ui update.
const stockAdjustments = []
querySnapshot.forEach((doc) => {
stockAdjustments.push({
id: doc.id, ...doc.data()
})
})
this.setState({ stockAdjustments });

Related

How to get AsyncStorage data without waiting in react native?

I have trouble trying to retrieve data from AsyncStorage, I can't directly assign a state like that, since it always returns undifined, how can I avoid that?
export default class ListTodo extends React.Component {
constructor(props) {
super(props);
this.state = {
data: {},
};
}
componentDidMount() {
//promise
GetDataAsyncStorage('#TODOS').then((data) => {
this.setState({
data: data,
});
});
}
render() {
const {data} = this.state;
console.log(data); // undifined
return (
<>
<Header />
<View>
<FlatList
data={data}
renderItem={({item}) => <TodoItemComponent data={item} />}
keyExtractor={(item) => item.id}
/>
</View>
</>
);
}
}
Here is my function to get data from asynStorage
export const GetDataAsyncStorage = async (key) => {
try {
let data = await AsyncStorage.getItem(key);
return {status: true, data: JSON.parse(data)};
} catch (error) {
return {status: false};
}
};
Add a state variable isLoading and toggle it after the data is got from AsyncStorage
snack: https://snack.expo.io/#ashwith00/async
code:
export default class ListTodo extends React.Component {
constructor(props) {
super(props);
this.state = {
data: {},
isLoading: false,
};
}
componentDidMount() {
this.getData();
}
getData = () => {
this.setState({
isLoading: true,
});
//promise
GetDataAsyncStorage('#TODOS').then((data) => {
this.setState({
data: data,
isLoading: false,
});
});
};
render() {
const { data, isLoading } = this.state;
return (
<View style={styles.container}>
{isLoading ? (
<ActivityIndicator />
) : data.data ? (
<FlatList
data={data}
renderItem={({ item }) => <Text>{item}</Text>}
keyExtractor={(item, i) => i.toString()}
/>
) : (
<Text>No Data Available</Text>
)}
</View>
);
}
}
Because AsyncStorage itself is asynchronous read and write, waiting is almost necessary, of course, another way to achieve, for example, to create a memory object, bind the memory object and AsyncStorage, so that you can read AsyncStorage synchronously.
For example, using the following development library can assist you to easily achieve synchronous reading of AsyncStorage react-native-easy-app
import { XStorage } from 'react-native-easy-app';
import { AsyncStorage } from 'react-native';
// or import AsyncStorage from '#react-native-community/async-storage';
export const RNStorage = {
token: undefined,
isShow: undefined,
userInfo: undefined
};
const initCallback = () => {
// From now on, you can write or read the variables in RNStorage synchronously
// equal to [console.log(await AsyncStorage.getItem('isShow'))]
console.log(RNStorage.isShow);
// equal to [ await AsyncStorage.setItem('token',TOKEN1343DN23IDD3PJ2DBF3==') ]
RNStorage.token = 'TOKEN1343DN23IDD3PJ2DBF3==';
// equal to [ await AsyncStorage.setItem('userInfo',JSON.stringify({ name:'rufeng', age:30})) ]
RNStorage.userInfo = {name: 'rufeng', age: 30};
};
XStorage.initStorage(RNStorage, AsyncStorage, initCallback);

How to filter pokemon in react native

I am trying to filter out pokemon in my searchbar component however when I type into the search bar the name of the component, nothing happens to the list. I have been searching online for solutions but other examples are too complex to implement into my code. I am consoling.log the input from the search bar component and it logs the input text. But just dont know how to filter out the pokemon. If anyone can help me I will really appreciate it!
// Home.js(Where pokemon ifo is coming from in the componentDidiMount abd then I pass down a function to the searchbar component)
import React, { useState } from "react";
import { View, Text , Button, FlatList, ActivityIndicator, TouchableOpacity } from "react-native";
import { GlobalStyles } from "../styles/GlobalStyles";
import PokeDetails from "./PokeDetails";
import SearchBarComponent from "../components/SearchBar";
import PokeBanner from "../components/PokeBanner";
class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
dataSource: [],
filteredPokemon:[]
}
}
componentDidMount() {
fetch(`https://pokeapi.co/api/v2/pokemon/?limit=27`)
.then((res)=> res.json())
.then((response)=> {
this.setState({
isLoading: false,
dataSource: response.results,
})
console.log("RESPONSE",response)
console.log("RESPONSE.RESSSULTS",response.results)
})
}
filterPokemon =(textToSearch)=> {
const allPokemon = [...this.state.dataSource];
this.setState({
dataSource: allPokemon.filter(pokemon=> pokemon.name.toLowerCase().includes(textToSearch.toLowerCase()))
});
console.log("TextToSearch",textToSearch)
}
render() {
const showIndicator = this.state.isLoading == true ? <ActivityIndicator size="large" color="#0000ff" /> : null;
return(
<View style={GlobalStyles.container}>
<SearchBarComponent filterPoke={this.filteredPokemon} style={GlobalStyles.searchBar}/>
<PokeBanner/>
<View style={GlobalStyles.activityIndicator}>{showIndicator}</View>
<View style={GlobalStyles.pokeFlatList}>
<FlatList
contentContainerStyle={{paddingBottom: 70}}
keyExtractor={(item, index) => item.name}
numColumns={3}
data={this.state.dataSource}
renderItem={({item})=>
<View style={{flex: 1,justifyContent:"center", alignItems:"center", flexDirection: "row", marginBottom: 50, padding: 10}}>
<TouchableOpacity onPress={()=> this.props.navigation.navigate('PokeDetails',
{item ,imageUrl: `https://projectpokemon.org/images/normal-sprite/${item.name}.gif`})}>
<PokeDetails imageUrl={`https://projectpokemon.org/images/normal-sprite/${item.name}.gif`} name={item.name}/>
</TouchableOpacity>
</View>
}/>
</View>
</View>
)
}
}
export default Home;
// SearchBarComponent(Where I take the function passed down as a prop and use it in the updateSearch method)
import React from "react";
import {View, StyleSheet } from "react-native";
import { SearchBar } from 'react-native-elements';
import { GlobalStyles } from "../styles/GlobalStyles";
class SearchBarComponent extends React.Component {
state = {
search: '',
};
updateSearch=()=> {
this.props.pokeFilter(this.state.search);
console.log(this.state.search)
}
render() {
const { search } = this.state;
console.log(search)
return (
<View style={GlobalStyles.searchBar}>
<SearchBar
placeholder="Search pokemon..."
onChangeText={text=>this.setState({search: text})}
value={search}
/>
</View>
);
}
}
export default SearchBarComponent;
[![enter image description here][1]][1]
You need to call your updateSearch function when the user wants to search for a pokemon.
There are multiple ways to do that such as you can keep a separate button to handle submit function or call updateSearch inside onChangeText of your search bar component as below,
<SearchBar
placeholder="Search pokemon..."
onChangeText={this.updateSearch}
value={search}
/>
now change your updateSearch to handle serach text
updateSearch = (text) => {
this.setState({ search: text });
this.props.pokeFilter(this.state.search);
}
Also change the props of SearchBarComponent component as (make sure to use correct name)
<SearchBarComponent pokeFilter={this.filterPokemon} style={GlobalStyles.searchBar}/>
But you have to keep a temp variable to store all your pokemons. Because you need to filter data from all pokemons when user midified the search field.
componentDidMount() {
fetch(`https://pokeapi.co/api/v2/pokemon/?limit=27`)
.then((res) => res.json())
.then((response) => {
this.setState({
isLoading: false,
// keep a temp to store all pokemons
pokemons: response.results,
dataSource: response.results,
});
});
}
Now you can use your filter function
filterPokemon = (textToSearch) => {
// load all pokemons from temp
const allPokemon = [...this.state.pokemons];
this.setState({
dataSource: allPokemon.filter(pokemon => pokemon.name.toLowerCase().includes(textToSearch.toLowerCase()))
});
}
Hope this helps you. Feel free for doubts.
You should set FilteredPokemon as all pokemon when you do the first petition and pass that state to the FlatList. That way you will only show the filtered pokemon:
Then when you modify the search you will just filter on the allPokemon state and set it to the filtered. Let me just show it:
componentDidMount() {
fetch(`https://pokeapi.co/api/v2/pokemon/?limit=27`)
.then((res)=> res.json())
.then((response)=> {
this.setState({
isLoading: false,
dataSource: response.results,
filteredPokemon: response.results,
})
console.log("RESPONSE",response)
console.log("RESPONSE.RESSSULTS",response.results)
})
}
filterPokemon =(textToSearch)=> {
const allPokemon = [...this.state.dataSource];
const filteredPokemon = allPokemon.filter((pokemon) =>
pokemon.name.toLowerCase().includes(textToSearch.toLowerCase()))
this.setState({
filteredPokemon
});
console.log("TextToSearch",textToSearch)
}
Any problem just let me know and I will be happy to help!

State in react component won't render

My react component won't load the data from the state, at all.
My loading function works as expected, as well as the rendering for it, however, even though the state updates (I logged it, it does return the expected data) nothing with render related to it.
If posts are empty, the <p>nothing</> tag does not show, and if there is data, it's not printed in the p tag nor is it loaded into my carousel.
import React, { Component } from 'react';
import { withFirebase } from '../Firebase';
import AliceCarousel from 'react-alice-carousel';
import 'react-alice-carousel/lib/alice-carousel.css';
import PostItem from '../Market/PostItem';
class LandingPosts extends Component {
constructor(props) {
super(props);
this.state = {
text: '',
loading: false,
posts: [],
limit: 5,
};
}
componentDidMount() {
this.onListenForMessages();
}
onListenForMessages = () => {
this.setState({ loading: true });
this.props.firebase
.collectionGroup('settings')
.where('homepagepost', '==', true)
.get().then(snapshot => {
let posts = [];
snapshot.forEach(doc => {
doc.ref.parent.parent.get().then(doc => {
posts.push({ ...doc.data(), uid: doc.id });
console.log(posts);
});
});
this.setState({ posts: posts.reverse(), loading: false });
});
};
responsive = {
0: { items: 1 },
1024: { items: 3 },
};
render() {
const { loading } = this.state;
return (
<div>
{loading && <div>Loading ...</div>}
{this.state.posts && (
<p>{this.state.posts[0]}</p>
)}
{!this.state.posts && (
<p>nothing</p>
)}
<AliceCarousel
items={this.state.posts.map(item => {return <PostItem data={item}/>})}
responsive={this.responsive}
autoPlayInterval={2000}
autoPlayDirection="rtl"
autoPlay={true}
fadeOutAnimation={true}
mouseDragEnabled={true}
disableAutoPlayOnAction={true}
buttonsDisabled={true}
/>
</div>
);
}
}
export default withFirebase(LandingPosts);
I think, following code is async in in your case.
doc.ref.parent.parent.get().then(doc => {
posts.push({ ...doc.data(), uid: doc.id });
console.log(posts);
});
If so try adding setting state in then or create array of promise like this.
posts.push(
doc.ref.parent.parent.get().then(doc => {
posts.push({ ...doc.data(), uid: doc.id });
console.log(posts);
});
)
Promise.all(posts).then((_posts)=>this.setState({ posts: _posts.reverse(), loading: false });)
I think you have to "repeat" your state declaration inside render.
Like this:
const {
text,
loading,
posts,
limit
} = this.state
At least that's how I have it in my components

How can I Update My isFetching Props to get Activity Spinner working?

I am making an Async API call from a React native Component. I would like to show my Activity Spinner until I get a response back. The setProps() function is deprecated. I know that I can pass a prop down from the AddressForm.js Parent Element when I render it. But how can I change the state of the parent element once I get a response to stop the Spinner Here is my code:
Address Form:
import React from 'react';
import {
View,
StyleSheet,
} from 'react-native';
import {
FormLabel,
FormInput,
Button,
} from 'react-native-elements'
import InfoButton from './InfoButton';
export default class AddressForm extends React.Component {
constructor(props){
super(props);
this.state = {
line1: '',
city: '',
state: '',
zip: '',
isFetching: false,
};
}
handleLine1 = (text) => {
this.setState({ line1: text })
}
handleCity = (text) => {
this.setState({ city: text })
}
handleState = (text) => {
this.setState({state: text })
}
handleZip = (text) => {
this.setState({ zip: text })
}
render() {
return (
<View style={styles.getStartedContainer}>
<FormLabel>Address Line 1</FormLabel>
<FormInput
onChangeText={this.handleLine1}
/>
<FormLabel>City</FormLabel>
<FormInput
onChangeText={this.handleCity}
/>
<FormLabel>State</FormLabel>
<FormInput
onChangeText={this.handleState}
/>
<FormLabel>Zip</FormLabel>
<FormInput
onChangeText={this.handleZip}
/>
<InfoButton // This is the child component
info={this.state}
API_KEY={this.props.API_KEY}
isFetching={this.state.isFetching}
/>
</View>
)
}
}
Here is the child component:
import React from 'react';
import {
View,
ActivityIndicator,
} from 'react-native'
import {
Button,
} from 'react-native-elements'
export default class InfoButton extends React.Component {
constructor(props){
super(props);
this.getVoterInfo = this.getVoterInfo.bind(this);
}
getVoterInfo(){
this.setProps({ isFetching: true}, () => console.log('Fetching Data: ' +this.props.isFetching));
fetch('https://www.googleapis.com/civicinfo/v2/representatives?key=' + API_KEY + '&address=' + newLine1 + '%20' + newCity + '%20' + newState + '%20')
.then((data) => {
results = data.json()
.then((data) => {
this.setState({data});
this.setProps({ isFetching:false });
console.log(this.state.data);
console.log('Ended Fetch:' + this.props.isFetching);
});
})
.catch((error) => {
console.log(error);
})
}
componentDidMount(){
console.log(this.state);
API_KEY = this.props.API_KEY;
}
componentDidUpdate(){
//Logic adds '%20' in place of spaces in address fields in order to correctly query the API
newLine1 = (this.props.info.line1.split(' ').join('%20'));
newCity = (this.props.info.city.split(' ').join('%20'));
newState = (this.props.info.state.split(' ').join('%20'));
// console.log(newLine1);
}
render() {
const myButton =
<Button
raised
icon={{name: 'cached'}}
title="Get Info"
onPress={this.getVoterInfo}
/>
const spinner = <ActivityIndicator size="large" color="#0000ff" />
return (
<View>
{this.props.isFetching === true ? spinner : myButton}
</View>
)
}
}
In order to achieve that you need to pass a function to your child component, via props, which will be called when you're done fetching in your child component.
<InfoButton // This is the child component
info={this.state}
API_KEY={this.props.API_KEY}
onFetchStart={() => {
this.setState({isFetching: true});
}}
onFetchEnd={() => {
this.setState({isFetching: false});
}}
/>
We pass here two functions, to know when we begin fetching and when we end.
In your InfoButton component, all you need to do is call these functions when needed, like that for example :
getVoterInfo(){
this.setState({ isFetching: true});
this.props.onFetchStart(); // HERE WE TELL OUR PARENT THAT OUR FETCHING HAS STARTED
fetch('https://www.googleapis.com/civicinfo/v2/representatives?key=' + API_KEY + '&address=' + newLine1 + '%20' + newCity + '%20' + newState + '%20')
.then((data) => {
results = data.json()
.then((data) => {
this.setState({data, isFetching: false});
this.props.onFetchEnd(); // HERE WE TELL OUR PARENT THAT OUR FETCHING HAS ENDED
});
})
.catch((error) => {
console.log(error);
})
}
And remember to use this.state.isFetching instead of this.props.isFetching in your InfoButton component !

React Native ListView - TypeError: Cannot read property 'cloneWithRows' of undefined (…)

Trying to create a list view from this api but I keep getting this error
TypeError: Cannot read property 'cloneWithRows' of undefined (…)
Probably an easy fix but i am confused!
Heres my code
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
ListView
} from 'react-native';
var heros = [];
class Heros extends Component {
constructor(props) {
super(props)
var dataSource = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 != r2})
this.state = {
peopleDataSource: dataSource.cloneWithRows(heros)
}
}
componentDidMount() {
this.getHeros(function(json){
heros = json;
this.setState = ({
datasource: this.state.dataSource.cloneWithRows(heros),
isLoading:false
})
}.bind(this));
}
getHeros(callback) {
var url = "https://api.lootbox.eu/xbl/us/Food%20Market/competitive-play/heroes";
fetch(url)
.then(response => response.json())
.then(json => callback(json))
.catch(error => console.log(error));
}
render() {
return (
<View>
<ListView
style={{marginTop: 100}}
dataSource={this.state.peopleDataSource}
renderRow={(person) => { return this._renderPersonRow(heros)
enableEmptySections={true} }} />
</View>
)
}
_renderPersonRow(person) {
return (
<View style={styles.personRow}>
<Text style={styles.PersonName}> {person.name} </Text>
</View>
)
}
}
module.exports = Heros;
Your data store variable is named peopleDataSource (declared in the constructor) yet you are trying to access it with this.state.dataSource. It should be this.state.peopleDataSource. Should be:
this.setState = ({
peopleDataSource: this.state.peopleDataSource.cloneWithRows(heros),
isLoading:false
})

Categories