AsyncStorage React Native save an Array - javascript

I tried saving an array, I tried to follow the documentation but failed miserably. How should I write it so that it doesn't give me various warnings and errors.
Errors :
got an [Object Object] when I try to set the item
Got an object instead of an array
Attempted to assign to read only property
expected a string, got an array
Here is the code : App.js
import React from "react";
import {
StyleSheet,
Text,
View,
TextInput,
ScrollView,
TouchableOpacity,
KeyboardAvoidingView,
AsyncStorage
} from "react-native";
import Note from "./app/components/note";
export default class App extends React.Component {
state = {
noteArray: [],
noteText: ""
};
render() {
let notes = this.state.noteArray.map((val, key) => {
return (
<Note
key={key}
keyval={key}
val={val}
deleteMethod={() => this.deleteNote(key)}
/>
);
});
return (
<KeyboardAvoidingView behavior="padding" style={styles.container}>
<View style={styles.header}>
<Text style={styles.headerText}>Tasker</Text>
</View>
<ScrollView style={styles.scrollContainer}>{notes}</ScrollView>
<View style={styles.footer}>
<TouchableOpacity
onPress={this.addNote.bind(this)}
style={styles.addButton}
>
<Text style={styles.addButtonText}>+</Text>
</TouchableOpacity>
<TextInput
style={styles.textInput}
placeholder="Enter Task..."
placeholderTextColor="white"
underlinedColorAndroid="transparent"
onChangeText={noteText => this.setState({ noteText })}
value={this.state.noteText}
/>
</View>
</KeyboardAvoidingView>
);
}
addNote() {
if (this.state.noteText) {
var d = new Date();
this.state.noteArray.push({
date:
d.getFullYear() +
"/" +
(d.getMonth() + 1) +
"/" +
d.getDate(),
note: this.state.noteText
});
this.setState({ noteArray: this.state.noteArray });
this.setState({ noteText: "" });
}
//AsyncStorage.setItem() How do I write it so no errors occur
alert({ noteArray: this.state.noteArray });
}
}
Extra Note : The Error is on Expo App on my phone both Android and iOS
Thanks in Advance!

Arrays and other objects need to be saved as strings in AsyncStorage.
AsyncStorage.setItem('arrayObjKey', JSON.stringify(myArray));
Also if you need to update the values in the array use AsyncStorage.multiMerge
From the React-Native docs:
Merges an existing key value with an input value, assuming both values are stringified JSON. Returns a Promise object.

Related

React Native: I am getting error while trying to get image from cloud firestore

I am getting source.uri should not be an empty string. How can i solve this?
I am using google cloud firestore to store all the images there and render it in my app, then this error shows up and i have no idea how to fix it, please help me solve this.
Here is my code:
import React from 'react';
import {
TouchableOpacity,
Image,
View,
Text
} from 'react-native';
import storage from '#react-native-firebase/storage';
import styles from '../styles/Android.style';
class Catalog extends React.Component {
constructor(props) {
super(props)
this.state = {
image : ''
}
}
retrieveImages = async () => {
const url = await storage()
.ref('/catalogs/web_hi_res_512.png')
.getDownloadURL();
this.setState({image : url})
}
componentDidMount() {
this.retrieveImages()
}
render() {
return (
<View style={styles.homeContainer}>
<TouchableOpacity style={styles.catalogItem}>
<View>
<Image
source={{uri: this.state.image}}
style={styles.catalogImage}
/>
</View>
<View style={styles.descriptionContainer}>
<Text style={styles.descriptionPrice}>Ini teks</Text>
<Text style={styles.descriptionAddress}>Ini deskripsi</Text>
</View>
</TouchableOpacity>
</View>
);
}
}
export default Catalog;
You have initialised with empty string (image : '') in your component state, so when component will load, source.uri will be initialised with '' because by that time you did not get the image from cloud firestore.
One solution can be to initialise the state with some default image and show that image in your Image component until the image from the cloud is downloaded.
As you have initialised your image state as an empty string, you are getting this error.
Conditionally rendering the image component would easily solve this. Just do:
<View style={styles.homeContainer}>
<TouchableOpacity style={styles.catalogItem}>
<View>
{image !== '' ? (
<Image
source={{uri: this.state.image}}
style={styles.catalogImage}
/>
) : null}
</View>
<View style={styles.descriptionContainer}>
<Text style={styles.descriptionPrice}>Ini teks</Text>
<Text style={styles.descriptionAddress}>Ini deskripsi</Text>
</View>
</TouchableOpacity>
</View>

Invariant Violation: Invariant Violation: Tried to get frame for out of range index NaN while rendering List

I was trying to develop a React Native app that has a Taxi search page with two search boxes, 'Pick Up' and 'Drop off'. I have used the google-places api for them and displayed the search results. My pick-up search box works perfectly fine. The same code when i use for my drop-off with few changes indicating 'Drop-off' throws error. I am unable to figure out what is going wrong. I face the following error:
Invariant Violation: Invariant Violation: Tried to get frame for out of range index NaN
in VirtualizedList (at FlatList.js:634) in FlatList (at List.js:12) in List (at connectStyle.js:392) in Styled(List) (at SearchResults/index.js:13) in RCTView (at View.js:35) in View (at View.js:10)
My Search Box code is the following:
import React from "react";
import {Text, PermissionsAndroid} from "react-native";
import styles from "./SearchBoxStyles";
import {View, InputGroup, Input} from "native-base";
import Icon from "react-native-vector-icons/FontAwesome";
export const SearchBox = ({getInputData, toggleSearchResultModal,getAddressPredictions}) =>{
function handleInput(key, val){
getInputData({
key,
value: val
});
getAddressPredictions();
}
return(
<View style={styles.searchBox}>
<View style={styles.inputWrapper}>
<Text style ={styles.label}>PICK UP</Text>
<InputGroup>
<Icon name="search" size={15} color="#FF5E3A"/>
<Input onFocus={()=>toggleSearchResultModal("pickUp")} style = {styles.inputSearch} placeholder="Choose pick up location" onChangeText={handleInput.bind(this,"pickUp")}/>
</InputGroup>
</View>
<View style={styles.inputWrapper}>
<Text style ={styles.label}>DROP OFF</Text>
<InputGroup>
<Icon name="search" size={15} color="#FF5E3A"/>
<Input onFocus={()=>toggleSearchResultModal("dropOff")} style = {styles.inputSearch} placeholder="Choose drop off location" onChangeText={handleInput.bind(this,"dropOff")}/>
</InputGroup>
</View>
</View>
);
}
export default SearchBox;
My Search results code is as the following:
import React from "react";
import {Text, PermissionsAndroid} from "react-native";
import styles from "./SearchResultsStyles";
import {View, List, ListItem, Left, Item, Body} from "native-base";
import Icon from "react-native-vector-icons/MaterialIcons";
export const SearchResults = ({predictions, getSelectedAddress}) =>{
function handleSelectedAddress(placeID){
getSelectedAddress(placeID)
}
return(
<View style={styles.searchResultsWrapper}>
<List
dataArray = {predictions}
renderRow ={(item)=>
<View>
<ListItem onPress={()=>handleSelectedAddress(item.placeID)} button avatar>
<Left style={styles.leftContainer}>
<Icon style={styles.leftIcon} name="location-on"/>
</Left>
<Body>
<Text style={styles.primaryText}>{item.primaryText}</Text>
<Text style={styles.secondaryText}>{item.secondaryText}</Text>
</Body>
</ListItem>
</View>
}
/>
</View>
);
}
export default SearchResults;
This is being rendered in:
import React from "react";
import {View} from "native-base";
import MapView from "react-native-maps";
import styles from "./MapContainerStyles"
import SearchBox from "../SearchBox";
import SearchResults from "../SearchResults";
const MapContainer = ({region, getInputData, toggleSearchResultModal, getAddressPredictions, resultTypes, predictions, getSelectedAddress}) =>{
return(
<View style={styles.container}>
<MapView
provider={MapView.PROVIDER_GOOGLE}
style={styles.map}
region={region}
>
<MapView.Marker
coordinate={region}
pinColor="green"
/>
</MapView>
<SearchBox
getInputData={getInputData}
toggleSearchResultModal={toggleSearchResultModal}
getAddressPredictions={getAddressPredictions}
/>
{ (resultTypes.pickUp || resultTypes.dropOff) &&
<SearchResults predictions={predictions} getSelectedAddress={getSelectedAddress}/>
}
</View>
)
}
export default MapContainer;
The following is the code related to my actions:
export function getInputData(payload){
return{
type:GET_INPUT,
payload
}
}
//toggle search result model
export function toggleSearchResultModal(payload){
return{
type:TOGGLE_SEARCH_RESULT,
payload
}
}
// GET ADDRESSES FROM GOOGLE PLACES
export function getAddressPredictions(){
return(dispatch, store)=>{
let userInput = store().home.resultTypes.pickUp ? store().home.inputData.pickUp : store().home.inputData.dropOff;
RNGooglePlaces.getAutocompletePredictions(userInput,
{
country:"us"
}
)
.then((results)=>
dispatch({
type:GET_ADDRESS_PREDICTIONS,
payload:results
})
)
.catch((error)=>console.log(error.message));
};
}
The following are the corresponding handlers:
function handleGetInputData(state, action){
const {key, value} =action.payload;
return update(state,{
inputData:{
[key]:{
$set:value
}
}
});
}
function handleToggleSearchResult(state, action){
if(action.payload === "pickUp"){
return update(state, {
resultTypes:{
pickUp:{
$set : true,
},
dropOff:{
$set : false
},
predictions:{
$set :{}
}
}
})
}
if(action.payload === "dropOff"){
return update(state, {
resultTypes:{
pickUp:{
$set : false,
},
dropOff:{
$set : true
}
},
predictions:{
$set :{}
}
})
}
}
function handleGetAddressPredictions(state,action){
return update(state,{
predictions:{
$set:action.payload
}
})
}
function handleGetSelectedAddress(state,action){
let selectedTitle = state.resultTypes.pickUp ? "selectedPickUp" : "selectedDropOff"
return update(state,{
selectedAddress:{
[selectedTitle]:{
$set:action.payload
}
},
resultTypes:{
pickUp: {
$set : false
},
dropOff:{
$set : false
}
}
})
}
I cant figure out the problem. Could someone help. Thanks in advance!!!
This is because of List is depreciated in react-native version 0.6. So you can use react-native components without using native base as follows.
import React from "react";
import { Text,ScrollView ,FlatList,View} from "react-native";
import Icon from "react-native-vector-icons/MaterialIcons";
import styles from "./searchResultsStyles";
export const SearchResults = ({ predictions, getSelectedAddress }) => {
function handleSelectedAddress(placeID) {
getSelectedAddress(placeID)
}
return (
< ScrollView style={styles.searchResultsWrapper} >
<FlatList
data={predictions}
renderItem={({ item }) => {
return <View style={styles.row}>
<View style={styles.leftContainer}>
<Icon style={styles.leftIcon} name="location-on" />
</View>
<Text style={styles.primaryText}>{item.primaryText}</Text>
<Text style={styles.secondaryText}>{item.secondaryText}</Text>
</View>
}}
/>
</ScrollView >
);
};
export default SearchResults;

Undefined is not an object on this.state object

I am setting up a React Native App. In this App i am trying to include a reusable Top Navigation Component, which accepts an array of strings and then renders them appropriately. When i am trying to compare the array string to a given string, it throws the following error:
Unhandeld JS Exception: TypeError: undefined is not an object (evaluating 'this.state.choseTopNavigation'
Inserting a string to compare works just fine
Setting up state object in constructor
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View, ScrollView, FlatList} from 'react-native';
import {Header, TopNavigationButton} from './components';
type Props = {};
export default class App extends Component<Props> {
constructor(props) {
super(props);
this.state = {
topNavigationOptions : [
{key: 'Quick Search'},
{key: 'Person Search'}],
chosenTopNavigation:
"Quick Search",
};
}
}
renderItem({item}){
if (item.key == this.state.chosenTopNavigation) {
return (
<TopNavigationButton>
{item.key}
</TopNavigationButton>
)
} else {
return (
<Text style = {styles.unchosenTextStyle}>
{item.key}
</Text>
)
}
}
render() {
return (
<View>
<Header>Header</Header>
<ScrollView horizontal = {true}>
<View style = {styles.navigationContainer}>
<TopNavigationButton>Quick Search</TopNavigationButton>
<Text style = {styles.unchosenTextStyle}>Person Search</Text>
</View>
</ScrollView>
<FlatList
horizontal
data={this.state.topNavigationOptions}
renderItem = {this.renderItem}
/>
</View>
);
}
}
Note This Works
renderItem({item} ){
if (item.key == "Quick Search") { //inserting a string directly works
return (
<TopNavigationButton>
{item.key}
</TopNavigationButton>
)
} else {
return (
<Text style = {styles.unchosenTextStyle}>
{item.key}
</Text>
)
}
}
Thanks a lot for your help!
Either you can make your function as arrow function like this:
renderItem = ({item}) => {
// your existing code
}
or you need to bind your function in constructor like this:
this.renderItem = this.renderItem.bind(this);

Invariant Violation: Invariant Violation: Text strings must be rendered within a <Text> component

creating an application in react-native running into this problem. basically I should create a page that prints the data of the user currently logged in to the database (firebase). I managed to create a sort of leaderboard that prints all users with data, on another page, but I can not figure out where I was wrong. can someone help me?
https://snack.expo.io/#khal_d/proj-p-3
import React, { Component } from 'react';
import { View, TouchableOpacity, StyleSheet, Button, Text, ScrollView, ListItem } from 'react-native';
import { Input, Card} from 'react-native-elements';
import * as firebase from 'firebase';
export default class User extends Component {
static navigationOptions = {
title: 'UserInfo',
};
state = {
data: [],
};
// Controllare qui
componentDidMount(){
//leggere array dal db
const currentUID = firebase.auth().currentUser.uid;
const path ="/users/" + currentUID;
const users = firebase.database().ref(path);
users.on("value", snap => {
console.log("log di snap" + snap);
//ciclo
var elenco = [];
snap.forEach(child => {
elenco.push({
name: child.val().name,
surname: child.val().surname,
email: child.val().email,
image: child.val().image,
})
});
console.log("altro log finale" + elenco);
this.setState({data:elenco})
});
}
// controllare fino a qua
render() {
return (
<ScrollView>
<View>
<Card> //fix evertything all'interno di card
{
this.state.data.map((l, i) => (
<ListItem
key={i}
leftAvatar={{ source: { uri: l.image } }}
title={l.name}
subtitle={l.surname}
/>
))
}
</Card>
</View>
</ScrollView>
);
}
}
I think the issue is because of the comment. In JSX, comment with // doesn't work. It will be treated as a text.
You have to change your comments like below which will fix your issue
{/* fix evertything all'interno di card */}
Just delete the comments and extra { }from your code in render() or use them as below. In JSX you cannot have // in render():
render() {
return (
<ScrollView>
<View>
<Card>
{ this.state.data &&
this.state.data.map((l, i) => (
<ListItem
key={i}
leftAvatar={{ source: { uri: l.image } }}
title={l.name}
subtitle={l.surname}
/>
))
}
</Card>
</View>
</ScrollView>
);
}
}
iOS has no problem with extra syntaxes in render(), but in android it will show that error.
Also because of asynchronous problem of setState, you will have and error of undefined is not an object. So it is better to have {this.state.data && condition on your ListItem. I hope I could help :)
ListItem should be imported from react-native-elements and not from react-native.

How to store the data when fetching from API

I create an application that retrieves data from a URL and display it. I'm a beginner and therefore I do not use Redux or other for the moment.
I managed to recover the data and display it on my application but I would like to use the local storage of the phone. I saw the examples for AsyncStorage on the documentation of the Expo website but I don't know how to adapt them to my code. In addition, do I have to display local storage data only when there is no internet connection? Or do I always still have to display them?
import React, {Component} from 'react';
import {ScrollView, View, FlatList, Image, ActivityIndicator} from 'react-native';
import {ListItem} from "react-native-elements";
import {createAppContainer, createStackNavigator} from "react-navigation";
import PronosticsDetailsScreen from "../../screens/PronosticsDetailsScreen";
import AppConfig from "../../AppConfig";
class MontanteTab extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
};
}
componentDidMount() {
return fetch('https://myurl.com')
.then((response) => response.json())
.then((responseJson) => {
this.setState({
isLoading: false,
dataSource: responseJson
}, function () {
});
})
.catch((error) => {
console.error(error);
});
}
render() {
if (this.state.isLoading === true) {
return (
<View style={{flex: 1, padding: 20}}>
<ActivityIndicator/>
</View>
)
}
return (
<View>
<ScrollView>
<View>
<FlatList
data={this.state.dataSource}
keyExtractor={(item, index) => index.toString()}
renderItem={({item}) => (
<ListItem
key={item.id}
roundAvatar
badge={{
value: item.statut,
textStyle: {color: '#fff'},
containerStyle: {marginRight: 0, backgroundColor: item.couleur}
}}
avatar={<Image
source={{uri: 'https://myurl.com/' + item.image}}
style={{borderRadius: 50, height: 50, width: 50}}/>}
title={item.competition}
subtitle={item.equipe_domicile + ' - ' + item.equipe_exterieur}
onPress={() => this.props.navigation.navigate('PronosticsDetails', {
item,
})}
/>
)}
/>
</View>
</ScrollView>
</View>
);
}
}
EDIT :
I tried this, but my data are not displayed :
import React, {Component} from 'react';
import {ScrollView, View, FlatList, Image, ActivityIndicator, AsyncStorage} from 'react-native';
import axios from "axios";
import {ListItem} from "react-native-elements";
import {createAppContainer, createStackNavigator} from "react-navigation";
import AppConfig from "../../AppConfig";
import Keys from "../../data/Constants/Storage";
import PronosticsDetailsScreen from "../../screens/PronosticsDetailsScreen";
class MontanteTab extends Component {
state = {
errors: null,
isLoading: true,
pronostics: [],
};
async componentDidMount() {
const isConnected = true;
if (isConnected) {
await this.loadPronostics();
}
try {
this.setState({pronostics: JSON.parse(await AsyncStorage.getItem(Keys.pronosticsMontante))});
} catch (error) {
console.log(error);
}
}
loadPronostics() {
this.setState({isLoading: true, error: null});
return axios.get(AppConfig.apiUrl + 'montante').then(async response => {
await AsyncStorage.setItem(Keys.pronosticsMontante, JSON.stringify(this.state.pronostics));
this.setState({isLoading: false});
}).catch(error => {
this.setState({isLoading: false, error: error.response});
console.log(error);
});
}
render() {
if (this.state.isLoading === true) {
return (
<View style={{flex: 1, padding: 20}}>
<ActivityIndicator/>
</View>
)
}
return (
<View>
<ScrollView>
<View>
<FlatList
data={this.state.pronostics}
keyExtractor={(item, index) => index.toString()}
renderItem={({item}) => (
<ListItem
key={item.id}
roundAvatar
badge={{
value: item.statut,
textStyle: {color: '#fff'},
containerStyle: {marginRight: 0, backgroundColor: item.couleur}
}}
avatar={<Image
source={{uri: AppConfig.imagesPronosticsUrl + item.image}}
style={{borderRadius: 50, height: 50, width: 50}}/>}
title={item.competition}
subtitle={item.equipe_domicile + ' - ' + item.equipe_exterieur}
onPress={() => this.props.navigation.navigate('PronosticsDetails', {
item,
})}
/>
)}
/>
</View>
</ScrollView>
</View>
);
}
}
You can use React Native AsyncStorage for storing data to local storage of the device.
import { AsyncStorage } from 'react-native'
Use this to save data
AsyncStorage.setItem('key', 'value');
AsyncStorage accepts value as an only string, so you may need to use stringify() before setting the value to AsyncStorage
And to retrieve data use
AsyncStorage.getItem('key');
Code:
const KEY = 'USER_DATA'
let keyValue = { name: yogi }
AsyncStorage.setItem(KEY,keyValue);
AsyncStorage.getItem(KEY).then(asyncStorageRes => {
console.log(JSON.parse(asyncStorageRes))
});
As this is a React Native project, I think AsyncStorage is what you're looking for. You can set the value in your empty setState callback in componentDidMount. If you only need to store the data at the end of a session, you can use AppState and set the value when nextState is background or inactive.
Use AsyncStorage.getItem() when AppState or nextState is active.
https://facebook.github.io/react-native/docs/appstate
https://facebook.github.io/react-native/docs/asyncstorage
Its depend on how frequently your listing data change,
if listing data is constant then you can store API response in local storage. and then display list data from local storage.
If listing data changing frequently, still you can use local storage. store data in local storage on API response.
On next time page load show data from local storage and also make API call to get latest data and on API response display data to list and also update in local storage.
Using this way user will not have to wait for API response.
For Storing Data use can use javascript syntax localStorage.setItem('key', 'apiresponse') and localStorage.getItem('key')
OR
can create Service class which will store API response in object, which can be also use in other files.

Categories