Having trouble trying to implement AsyncStorage in React Native - javascript

I am using react native to build an app and the only problem i am having is that i have a progress bar that keeps track of the users progress but when I close the app completely and open it back up everything resets to its original data.So I turned to AsyncStorage to hold my data in but I am having trouble trying to figure out how to use it in my code, if anyone could help that would be great.
*UPDATE:
I tried to implement asyncstorage and the data seems to be sticking when I fully close the app but I have it so every time I press a button the progress bar will go up %20 and for instance if its at %80 and I reload the app it will show it as %60 instead, I was wondering if anyone could help me fix this issue to have the progress bars percentage stay the same after reloading or closing the app.
'use strict';
var React = require('react-native');
var ProgressBar = require('react-native-progress-bar');
var {
AppRegistry,
AsyncStorage,
StyleSheet,
Text,
View,
TouchableHighlight
} = React;
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#FFF',
},
button: {
alignSelf: 'center',
marginTop: 50,
width: 100,
height: 50,
backgroundColor: '#0059FF',
borderRadius: 8,
borderWidth: 2,
borderColor: '#0059FF'
},
buttonClear: {
alignSelf: 'center',
marginTop: 10,
width: 100,
height: 50,
backgroundColor: '#3B3A3A',
borderRadius: 8,
borderWidth: 2,
borderColor: '#3B3A3A'
},
buttonText: {
fontSize: 18,
textAlign: 'center',
lineHeight: 33,
color: '#FFF',
}
});
var PROGRESS = 0;
class BasicStorageExample extends React.Component {
constructor(props) {
super(props);
this.state = {
progress: PROGRESS
}
}
componentDidMount() {
AsyncStorage.getItem('progressbar')
.then((value) => {
JSON.parse(value);
this.setState({
progress: value
});
console.log('Progress on load: ' + value);
})
.done();
}
onButtonPress() {
AsyncStorage.setItem('progressbar', JSON.stringify(PROGRESS))
.then(() => {
JSON.parse(PROGRESS);
this.setState({
progress: PROGRESS += 0.2
});
console.log('Progress on Button Press: ' + PROGRESS);
})
.done();
}
onButtonClearPress() {
AsyncStorage.setItem('progressbar', JSON.stringify(PROGRESS))
.then(() => {
JSON.parse(PROGRESS);
PROGRESS = 0;
this.setState({
progress: 0
});
})
.done();
}
render() {
return (
<View style={styles.container}>
<ProgressBar
fillStyle={{}}
backgroundStyle={{backgroundColor: '#cccccc', borderRadius: 2}}
style={{marginTop: 10, width: 300}}
progress={this.state.progress} />
<TouchableHighlight
ref="button"
style={styles.button}
underlayColor='#002C7F'
onPress={this.onButtonPress.bind(this)}>
<Text style={styles.buttonText}>Done</Text>
</TouchableHighlight>
<TouchableHighlight
style={styles.buttonClear}
underlayColor='#002C7F'
onPress={this.onButtonClearPress.bind(this)}>
<Text style={styles.buttonText}>Clear</Text>
</TouchableHighlight>
</View>
);
}
};
AppRegistry.registerComponent('BasicStorageExample', () => BasicStorageExample);

Just call AsyncStorage.setItem('some-id', someVar) to set it then AsyncStorage.getItem('some-id') to retrieve. It's similar to localStorage. There's a full API and example in the documentation:
https://facebook.github.io/react-native/docs/asyncstorage.html

For store data
AsyncStorage.setItem('progressbar', JSON.stringify(PROGRESS))
For getting Data call asynchronously.
async componentWillMount() {
const result = await AsyncStorage.getItem('progressbar');
// you will get value. check your conditions here
}

Related

React Native and Firebase Real Time Database

I am having 2 problems using React Native and Firebase Real Time Database.
When I add something to the list with the text input, all the list itens are duplicated except the item that I just added, this problem is only solved when I refresh the app screen.
When I remove something from firebase dashboard or other client, the list is not updated real time.
import React, {useState, Component} from 'react';
import {
Text,
View,
Switch,
StyleSheet,
FlatList,
TextInput,
Button,
TouchableOpacity,
SafeAreaView,
VirtualizedList,
} from 'react-native';
import database from '#react-native-firebase/database';
class MenuBreakFastScreen extends React.Component {
state = {newItem: ''};
state = {itens: []};
componentDidMount() {
let dbRef = database().ref('/cafe/itens/');
this.listenerFirebase(dbRef);
}
listenerFirebase(dbRef) {
dbRef.on('value', dataSnapshot => {
const newItens = JSON.parse(JSON.stringify(this.state.itens));
dataSnapshot.forEach(child => {
newItens.push({
name: child.val().name,
key: child.key,
});
this.setState({itens:newItens});
});
});
}
addItem() {
if (this.state.newItem === '') {
return;
}
database().ref('/cafe/itens/').push({
name: this.state.newItem,
});
this.setState({
newItem: '',
});
}
render() {
const {itens} = this.state;
const {newItem} = this.state;
const renderItem = ( {item}) => {
return(
<ItemAsset title={item.name}/>
);
}
return (
<SafeAreaView
style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<FlatList
data={itens}
renderItem={renderItem}
keyExtractor={item => item.key}
/>
<SafeAreaView style={{flexDirection: 'row'}}>
<TextInput
style={styles.input}
onChangeText={text =>
this.setState({
newItem: text,
})
}
value={newItem}
/>
<TouchableOpacity style={styles.Botao} onPress={() => this.addItem()}>
<Text style={styles.BotaoTexto}>+</Text>
</TouchableOpacity>
</SafeAreaView>
</SafeAreaView>
);
}
}
const styles = StyleSheet.create({
texto: {
fontSize: 35,
},
input: {
color: '#000',
fontSize: 22,
borderWidth: 1,
flex: 8,
margin: 10,
},
BotaoTexto: {
color: '#fff',
fontSize: 22,
},
Botao: {
backgroundColor: '#000',
marginTop: 10,
padding: 10,
flex: 1,
alignItems: 'center',
margin: 10,
},
ListaContainer: {
flexDirection: 'row',
backgroundColor: '#000',
flex: 1,
},
item: {
backgroundColor: '#000',
padding: 20,
marginVertical: 8,
marginHorizontal: 16,
flexDirection: 'row',
},
title: {
color: '#ffff',
fontSize: 32,
},
});
const ItemAsset = ( {title} ) => {
return(
<View style={styles.item}>
<Text style={styles.title}>{title}</Text>
</View>
);
}
export default MenuBreakFastScreen;
When you are listen for real time changes on real-time database it will send all the items with snapshot when any data is changed. That happens because you are listen for whole list, not only for a single item. Therefore you do not need to get the current list from state. You just have to set the state with retrieved data.
listenerFirebase(dbRef) {
dbRef.on('value', dataSnapshot => {
const newItens = []; // This should be initially empty array. That's all.
dataSnapshot.forEach(child => {
newItens.push({
name: child.val().name,
key: child.key,
});
});
this.setState({itens:newItens});
});
}
After correcting this part the error you got when removing data will be also resolved.

Add subobject using setState method

I'm trying to add a new subobject to an existing object using the method this.setState in a expo application. The subobject are added after a click of a button, that update the fields that compons the subobject.
This is my code:
import React, { Component } from 'react';
import { View, Text, StyleSheet, TouchableOpacity, ImageBackground } from 'react-native';
import CenteredButton from '../components/CenteredButton';
import { Actions } from 'react-native-router-flux';
var t = require('tcomb-form-native');
var _ = require('lodash');
const Form = t.form.Form;
const stylesheet = _.cloneDeep(t.form.Form.stylesheet);
stylesheet.textbox.normal.borderColor = '#b3b3b5';
stylesheet.textbox.normal.fontFamily = 'RobotoThin';
stylesheet.textbox.normal.backgroundColor = '#fdfdfd';
stylesheet.textbox.normal.fontSize = 18;
stylesheet.textbox.normal.borderWidth = 0.6;
stylesheet.textbox.normal.borderRadius = 10;
stylesheet.textbox.error.fontFamily = 'RobotoThin';
stylesheet.textbox.error.backgroundColor = '#fdfdfd';
stylesheet.textbox.error.fontSize = 18;
stylesheet.textbox.error.borderWidth = 0.6;
stylesheet.textbox.error.borderRadius = 10;
const Email = t.refinement(t.String, email => {
const regex = /[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*#(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/;
return regex.test(email);
});
const EmailTo = t.struct({
emailPerson: Email,
emailInsurance: t.maybe(Email)
});
const options = {
auto: 'none',
stylesheet: stylesheet,
fields: {
emailPerson: {
placeholder: 'Email personale',
autoCapitalize: 'none',
autoCorrect: false,
},
emailInsurance: {
placeholder: 'Email Assicurazione',
autoCapitalize: 'none',
password: true,
}
}
}
export default class NessunProblema extends Component {
constructor(props) {
super(props);
this.state = {
emails: {
emailPerson: '',
emailInsurance: ''
},
ascertainment: { }
}
}
componentDidMount() {
this.setState({ ascertainment: this.props.ascertainment });
}
_onChange = (emails) => {
this.setState({ emails });
}
_handle = () => {
const value = this.refs.form.getValue();
if ( value ) {
this.setState(prev => ({
ascertainment: {
...prev.ascertainment,
emails: {
...prev.ascertainment.emails,
emailPerson: value.emailPerson,
emailInsurance: value.emailInsurance
}
}
}));
}
console.log(this.state.emails);
console.log(this.state.ascertainment);
}
render() {
return (
<View style={{flex: 1, backgroundColor: 'white' }}>
<ImageBackground source={require('../images/NoProblem.png')} style={styles.backgroundImage}>
<View style={{ flex: 2, alignItems: 'center', justifyContent: 'center', width: '100%', paddingHorizontal: 20, top: 10}}>
<Text style={styles.domanda}>
Text
</Text>
<Text style={styles.domanda2}>
Text
</Text>
</View>
<View style={{padding: 20}}>
<Form
ref='form'
options={options}
type={EmailTo}
value={this.state.emails}
onChange={this._onChange}
/>
</View>
<CenteredButton
next={ this._handle }
/>
</ImageBackground>
</View>
)
}
}
const styles = StyleSheet.create({
domanda: {
color: '#00b0ff',
textAlign: 'center',
fontSize: 44,
fontFamily: 'RobotoRegular',
alignItems: 'center',
justifyContent: 'center',
padding: 20
},
domanda2: {
color: 'black',
textAlign: 'center',
fontSize: 22,
fontFamily: 'RobotoRegular',
alignItems: 'center',
justifyContent: 'center',
padding: 20
},
testoRosso: {
color: '#f32a19',
fontFamily: 'RobotoRegular',
},
backgroundImage: {
flex: 1,
resizeMode: 'cover'
},
textInput: {
width: '100%',
paddingHorizontal: 15,
height: 40,
marginBottom: 20,
fontSize: 18,
borderWidth: 0.6,
borderColor: 'black',
borderRadius: 10,
color: 'black',
fontFamily: 'RobotoThin',
backgroundColor: 'white'
},
});
I noticed that, if I click TWO times the button AVANTI I obtein the correct result. But, WHY?
I follow this answer but doens't resolve the problem.
The problem is that setState() is not synchronous, the values are updated asynchronously
setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value. There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains.
_handle = () => {
const value = this.refs.form.getValue();
if ( value ) {
this.setState(prev => ({
ascertainment: {
...prev.ascertainment,
emails: {
...prev.ascertainment.emails,
emailPerson: value.emailPerson,
emailInsurance: value.emailInsurance
}
}
}));
}
console.log(this.state.emails); // will be the emails in the previous state, since setState has not been called yet by react
console.log(this.state.ascertainment); // if you click twice, you are still getting the `last` state, but since it is the same as the state you are setting the second time, you get the wrong idea that it is being set if you click twice!
}

Not able to fetch image from API in React Native

I am a beginner on React Native and was working on an app that fetches text and images from my API and displays it on the screen. The text is displayed but the image is not displayed. No errors. I have only one main file : App.js with the API call being
https://www.teanewsnetwork.com/api/fetcharticles.php?code=Me*z6Pcw9e1$CQ)YWgMlFe%nv(Hq)lhw&starting=0&limit=20
App.js :
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
import React, { Component } from 'react';
import {
View,
Text,
ActivityIndicator,
ScrollView,
StyleSheet,
Image,
} from 'react-native';
import Img from 'react-image';
let li = 'https://www.teanewsnetwork.com/profileicons/';
let bean = 'azure.jpg';
export default class App extends Component {
state = {
loading: true,
error: false,
posts: [],
};
componentWillMount = async () => {
try {
const response = await fetch(
'https://www.teanewsnetwork.com/api/fetcharticles.php?code=Me*z6Pcw9e1$CQ)YWgMlFe%nv(Hq)lhw&starting=0&limit=40'
);
const posts = await response.json();
this.setState({ loading: false, posts });
} catch (e) {
this.setState({ loading: false, error: true });
}
};
renderPost = ({ id, title, content, authorimage, authorname }, i) => {
let b = { authorname };
return (
<View style={styles.postContent}>
<Text>{authorname}</Text>
<Image
source={{
uri: 'https://teanewsnetwork.com/profileicons/',
}}
style={{ width: 40, height: 40 }}
/>
<Text style={styles.postauthor}>{title}</Text>
<Text style={styles.postBody}>{content}</Text>
</View>
);
};
render() {
const { posts, loading, error } = this.state;
if (loading) {
return (
<View style={styles.center}>
<ActivityIndicator animating={true} />
</View>
);
}
if (error) {
return (
<View style={styles.center}>
<Text>Failed to load posts!</Text>
</View>
);
}
return (
<ScrollView style={styles.container}>
{posts.map(this.renderPost)}
</ScrollView>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
postauthor: {
flex: 1,
borderBottomWidth: 1,
borderBottomColor: '#EEE',
paddingVertical: 25,
paddingRight: 15,
},
postContent: {
flex: 1,
borderBottomWidth: 1,
borderBottomColor: '#EEE',
paddingVertical: 25,
paddingRight: 15,
left: 10,
},
postBody: {
marginTop: 10,
fontSize: 12,
color: 'lightgray',
left: 10,
},
center: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
I want to display my images below every name which are to be taken from the link
www.teanewsnetwork.com/profileicons/{{authorimage}}
Where {{authorimage}} is given in the API. But this doesn't seem to work.
Thanks is advance!
You can use template literals in JavaScript. This will append the authtorimage to the end of the string.
<Image
source={{
uri: `https://teanewsnetwork.com/profileicons/${authorimage}`,
}}
style={{ width: 40, height: 40 }}/>
Template literals are enclosed by the back-tick (```) (grave accent) character instead of double or single quotes. Template literals can contain placeholders. These are indicated by the dollar sign and curly braces (${expression}). The expressions in the placeholders and the text between them get passed to a function.
You are doing it very correctly except in two places which I have pointed below:
- While importing the react native components you are also doing:
import Img from 'react-image';
This is unnecessary as react native itself provides Image components.
- Also, you need to pass value of authorimage in source of the image as:
<Image
source= {{
uri: `https://teanewsnetwork.com/profileicons/${authorimage}`,
}}
style={{height: 40, width: 40}}
/>
Here, is the link of working snack.
If you want to learn more about how images works in react native, you can visit this link.

How to add function to dynamically created list view Buttons in react native?

how to add function to dynamically created list view Buttons in react native?
I get the error "undefined is not a function (evaluating '_this4.fun()')"
import React, { Component } from 'react';
import {
StyleSheet,
Text,
ListView,
View,
Alert,
ToolbarAndroid,
TouchableOpacity
} from 'react-native';
var _navigator;
export default class Quotes extends Component {
constructor(props) {
super(props);
this.state = {
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2,
}),
loaded: false,
};
}
fun(){
this.setState({
loaded: true
});
}
getMoviesFromApiAsync() {
return fetch('https://facebook.github.io/react-native/movies.json')
.then((response) => response.json())
.then((responseJson) => {
console.log(responseJson);
this.setState({dataSource:this.state.dataSource.cloneWithRows(responseJson.movies),
});
return responseJson.movies;
})
.catch((error) => {
console.error(error);
});
}
render () {
_navigator = this.props.navigator;
return (
<View style={styles.parentContainer}>
<View style={styles.container}>
<TouchableOpacity
style={styles.button}
onPress={()=>this.getMoviesFromApiAsync()}>
<Text style={styles.buttonText}>testing network</Text>
</TouchableOpacity>
</View>
<Text style={styles.bigblue}>Dynamiclly created buttons below</Text>
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderMovie}
style={styles.listView}/>
</View>
)
}
I need the function fun() to be called when clicked, after dynamically created buttons in list view are created
I noticed thatfun() does not call only when I create dynamically created buttons and if I create a static button I can call it normally.
renderMovie(moviess) {
return (
<View style={styles.container}>
<View>
<TouchableOpacity
style={styles.button}
onPress={()=>this.fun()}>
<Text style={styles.buttonText}>{moviess.releaseYear}</Text>
</TouchableOpacity>
<Text>{moviess.releaseYear}</Text>
</View>
</View>
);
}
}
var styles = StyleSheet.create({
parentContainer: {
flex: 1,
},
container: {
justifyContent: 'center',
backgroundColor: '#F5FCFF',
flexDirection: 'row',
},
bigblue: {
color: 'grey',
fontWeight: 'bold',
fontSize: 20,
},
toolbar: {
height: 56,
backgroundColor: '#4883da',
},
buttonText: {
fontSize: 18,
color: 'white',
alignSelf: 'center'
},
button: {
height: 70,
width: 70,
backgroundColor: '#FFC0CB',
alignSelf: 'center',
marginRight: 10,
marginLeft: 10,
marginTop: 10,
marginBottom: 15,
borderRadius: 50,
borderWidth: 0.7,
borderColor: '#d6d7da',
justifyContent: 'center'
}
});
I get the error ** undefined is not a function (evaluating '_this4.fun()')**
Somehow you are loosing this inside of renderMovie(moviess) as it is bound to the global scope and not the class Quotes anymore.
if you add renderRow={this.renderMovie.bind(this)} it should work. (instead of renderRow={this.renderMovie})
you could also add in the constructor : this.renderMovie = this.renderMovie.bind(this);

React Native progress bar percentage not staying the same after closing the app

Every time I press a button the progress bar will go up %20 but the problem is for instance if its at %80 and I reload the app it will show it as %60 instead, I was wondering if anyone could help me fix this issue to have the progress bars percentage stay the same after reloading or closing the app.
'use strict';
var React = require('react-native');
var ProgressBar = require('react-native-progress-bar');
var {
AppRegistry,
AsyncStorage,
StyleSheet,
Text,
View,
TouchableHighlight
} = React;
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#FFF',
},
button: {
alignSelf: 'center',
marginTop: 50,
width: 100,
height: 50,
backgroundColor: '#0059FF',
borderRadius: 8,
borderWidth: 2,
borderColor: '#0059FF'
},
buttonClear: {
alignSelf: 'center',
marginTop: 10,
width: 100,
height: 50,
backgroundColor: '#3B3A3A',
borderRadius: 8,
borderWidth: 2,
borderColor: '#3B3A3A'
},
buttonText: {
fontSize: 18,
textAlign: 'center',
lineHeight: 33,
color: '#FFF',
}
});
var PROGRESS = 0;
class BasicStorageExample extends React.Component {
constructor(props) {
super(props);
this.state = {
progress: PROGRESS
}
}
componentDidMount() {
AsyncStorage.getItem('progressbar')
.then((value) => {
JSON.parse(value);
this.setState({
progress: value
});
console.log('Progress on load: ' + value);
})
.done();
}
onButtonPress() {
AsyncStorage.setItem('progressbar', JSON.stringify(PROGRESS))
.then(() => {
JSON.parse(PROGRESS);
this.setState({
progress: PROGRESS += 0.2
});
console.log('Progress on Button Press: ' + PROGRESS);
})
.done();
}
onButtonClearPress() {
AsyncStorage.setItem('progressbar', JSON.stringify(PROGRESS))
.then(() => {
JSON.parse(PROGRESS);
PROGRESS = 0;
this.setState({
progress: 0
});
})
.done();
}
render() {
return (
<View style={styles.container}>
<ProgressBar
fillStyle={{}}
backgroundStyle={{backgroundColor: '#cccccc', borderRadius: 2}}
style={{marginTop: 10, width: 300}}
progress={this.state.progress} />
<TouchableHighlight
ref="button"
style={styles.button}
underlayColor='#002C7F'
onPress={this.onButtonPress.bind(this)}>
<Text style={styles.buttonText}>Done</Text>
</TouchableHighlight>
<TouchableHighlight
style={styles.buttonClear}
underlayColor='#002C7F'
onPress={this.onButtonClearPress.bind(this)}>
<Text style={styles.buttonText}>Clear</Text>
</TouchableHighlight>
</View>
);
}
};
AppRegistry.registerComponent('BasicStorageExample', () => BasicStorageExample);
The problem is that you are getting the old value of PROGRESS (before you increment by 0.2) and setting this value to the item progressbar.
So, every time you reload, React runs the componentDidMount function, and since you set progressbar to the old value of PROGRESS, it will always show it one increment behind what you see in the view.
Try changing your onButtonPress() to this:
onButtonPress() {
PROGRESS += 0.2;
AsyncStorage.setItem('progressbar', JSON.stringify(PROGRESS))
... // continues normally

Categories