I'm new to react native. I am trying to get 'Key' and 'Key2' with 2 properties myKey' and 'costKey', when calling in TextInput.
The 2 textinput values are saved in async storage keys. Now I am trying to call them with 2 different properties, which are 'myKey' and 'costKey'.
Please suggest, how to get two saved keys with 2 properties when calling.
//AddScreen.js
import React, { Component } from 'react';
import { AppRegistry, AsyncStorage, View, Text, Button, TextInput, StyleSheet, Image, TouchableHighlight, Linking } from 'react-native';
import styles from '../components/styles';
import { createStackNavigator } from 'react-navigation';
import History from '../components/History';
export default class AddScreen extends Component {
constructor(props) {
super(props);
this.state = {
myKey: '',
costKey: '',
text1: '',
text2: '',
}
}
async getKey() {
try {
const value = await AsyncStorage.getItem('#MySuperStore:key');
const key = await AsyncStorage.getItem('#MySuperStore:key');
const key1 = await AsyncStorage.getItem('#MySuperStore:key1');
const key2 = await AsyncStorage.getItem('#MySuperStore:key2');
this.setState({ myKey: key }, { costKey: key2 });
} catch (error) {
console.log("Error retrieving data" + error);
}
}
async saveKey(text1, text2) {
key = text1 + text2;
try {
await AsyncStorage.setItem('#MySuperStore:key', key);
await AsyncStorage.setItem('#MySuperStore:key1', text1);
await AsyncStorage.setItem('#MySuperStore:key2', text2);
} catch (error) {
console.log("Error saving data" + error);
}
}
async resetKey() {
try {
await AsyncStorage.removeItem('#MySuperStore:key');
const value = await AsyncStorage.getItem('#MySuperStore:key');
this.setState({ myKey: value }, { costKey: value });
} catch (error) {
console.log("Error resetting data" + error);
}
}
render() {
const { navigate } = this.props.navigation;
return (
<View style={styles.MainContainer}>
<TextInput
style={styles.formInput}
placeholder="Enter key you want to save!"
value={this.state.myKey}
onChangeText={(value) => this.setState({ text1: value })}
/>
<TextInput
style={styles.formInput}
placeholder="Enter key you want to save!"
value={this.state.costKey}
onChangeText={(value) => this.setState({ text2: value })}/>
<Button
onPress={() => this.saveKey(this.state.text1, this.state.text2)}
title="Save key"
/>
<Button
style={styles.formButton}
onPress={this.getKey.bind(this)}
title="Get Key"
color="#2196f3"
accessibilityLabel="Get Key"
/>
<Button
style={styles.formButton}
onPress={this.resetKey.bind(this)}
title="Reset"
color="#f44336"
accessibilityLabel="Reset"
/>
<Text style={styles.instructions}>
Stored key is = {this.state.myKey}
</Text>
<Text style={styles.instructions}>
Stored key is = {this.state.costKey}
</Text>
</View>
)
}
}
Please suggest by taking my example, that how to call two different property separately.
Your request is not so clear, but in a brief analysys I just noticed that you're using setState() incorrectly (both in getKey() and resetKey()). You declared your state as an object with some keys, so you are supposed to modify it passing it a new object with the same structure:
this.setState({
...this.state,
myKey: key,
costKey: key2
});
Related
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);
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!
)
I am programming a Notes App. The Note is saved when navigating back to the homescreen (away from Note Edit Component). The List of the Notes Titles (in HomeScreen) is updated onWillFocus. The Problem is the note save is async and takes some time... so onWillFocus updates the list BEFORE the note is saved. Now i want to call the list update manually when the note save resolves. But I have no idea how to do that.
I have one db file where all database functions live in. And two components.
Now i need to call a function in the HomeScreen Component from the db file.
that is my db file (removed other functions)
//db imports and making a const db
export function updateNote(updateStuff) {
db.get(_id).then(function(doc) {
return db.put({
//updateStuff
});
}).then(async function(response) {
console.log(response)
//here i need to call the function
}).catch(function (err) {
console.log(err);
});
}
and this is my HomeScreen Component
import React from 'react';
import {
//all elements
} from 'react-native';
import { NavigationEvents } from 'react-navigation';
import { putNote, getAllNotes, deleteAllNotes } from './db/db.js';
export default class HomeScreen extends React.Component {
state = {
notes: [],
}
async renderAllNotes() {
let result = await getAllNotes();
this.setState({notes: result.rows});
}
render() {
return (
<View style={styles.container}>
<NavigationEvents
onWillFocus={() => this.renderAllNotes()}
/>
<FlatList
//Flat List Code
/>
</View>
);
}
}
Here is my Note Edit component:
import React from 'react';
import {
//stuff
} from 'react-native';
import { updateNote, getNote, getAllNotes } from './db/db.js';
export default class NoteScreen extends React.Component {
state = {
_id: this.props.navigation.getParam('_id'),
}
updateThisNote() {
updateNote(this.state._id, this.state.title, this.state.content, this.state.createdAt);
}
componentWillUnmount() {
this.updateThisNote();
}
render() {
return (
<View style={styles.container}>
<TextInput
style={{height: 40, borderColor: 'gray', borderWidth: 1}}
onChangeText={(text) => this.setState({ title: text })}
value={this.state.title}
/>
<TextInput
style={{height: 40, borderColor: 'gray', borderWidth: 1}}
onChangeText={(text) => this.setState({ content: text })}
value={this.state.content}
/>
<Button
title='update Note'
onPress={() => this.updateThisNote()}
/>
</View>
);
}
}
and now renderAllNotes should be calln when updateNote resolves.
I already tried importing the HomeScreen class in the db file and calling the function as well as trying to export the render allNotes function an import it in the db file. Without success ;(
Thank you for every help ;)
EDIT:
async putNoteAndPushRoute() {
let resolve = await putNote("");
this.props.navigation.navigate('Note', {
_id: resolve.id,
renderAllNotes: this.renderAllNotes.bind(this),
});
}
Error Message: _this2.props.renderAllNotes is not a function
You can pass the this.renderAllNotes() to your edit component.
Like this.
...
renderAllNotes(){
....
}
...
render() {
const { navigate } = this.props.navigation;
return (
<View>
<Button onPress={() =>{
navigate('Edit',{
renderAllNotes: this.renderAllNotes.bind(this)
});
}} />
</View>
)
}
...
And then inside your edit,
you can just call the renderAllNotes after the note is updated. But you need to change your updateNote to return a promise
updateThisNote(){
// Make sure your updateNote returns a promise
updateNote(this.state._id, this.state.title,
this.state.content, this.state.createdAt)
.then(() => {
const { params} = this.props.navigation.state;
params.renderAllNotes();
});
}
componentWillUnmount() {
this.updateThisNote();
}
you can change your update function to return a promise
export function updateNote(updateStuff) {
return new Promise(function(resolve, reject) {
db.get(_id).then(function(doc) {
return db.put({
//updateStuff
});
}).then(async function(response) {
console.log(response)
//resolve it here
resolve();
}).catch(function (err) {
console.log(err);
});
}
}
This would solve your issue.
I'm new to react native. I am trying to get 'Key' without using the onpress in button.
I just want to get a 'key', when i could open component. How it could be possible?
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
TextInput,
Button,
View,
AsyncStorage
} from 'react-native';
export default class History extends Component {
constructor(props) {
super(props);
this.state = {
myKey: null
}
}
async getKey() {
try {
const value = await AsyncStorage.getItem('#MySuperStore:key');
this.setState({ myKey: value });
} catch (error) {
console.log("Error retrieving data" + error);
}
}
render() {
return (
<View style={styles.container}>
<Button
style={styles.formButton}
onPress={this.getKey.bind(this)}
title="Get Key"
color="#2196f3"
accessibilityLabel="Get Key"
/>
<Text >
Stored key is = {this.state.myKey}
</Text>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
padding: 30,
flex: 1,
backgroundColor: '#F5FCFF',
},
});
I am able to get a key with onpress but i want without onpress. Please suggest.
You can simply get your key value with componentDidMount. As you should know, when the App runs and comes to the current screen, there is a flow of methods will be called before and after rendering the render function. So ComponentDidMount comes after render function is called. So since you need to only display the key value, just follow below code.
constructor(props) {
super(props);
this.state = {
myKey:''
}
}
getKey = async() => {
try {
const value = await AsyncStorage.getItem('#MySuperStore:key');
this.setState({ myKey: value });
} catch (error) {
console.log("Error retrieving data" + error);
}
}
componentDidMount(){
this.getKey();
}
render() {
return (
<View style={styles.container}>
<Button
style={styles.formButton}
onPress={this.getKey.bind(this)}
title="Get Key"
color="#2196f3"
accessibilityLabel="Get Key"
/>
<Text >
Stored key is {this.state.myKey}
</Text>
</View>
)
}
Whenever render function being called, in that time, still key value is not set. So, this.state.myKey would be just Stored key is. But after that, once the componentDidMount called, it sets the myKey value and change states. Which will trig render function to render everything again. That would show your key value within the text component eventually without touching any button.
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 !