Unhandled Promise Rejection : Existing value for key "Favorites" must be of type null or array, revived string - react-native

my problem is quite simple but I'm new to react native dev. I'd like to save multiple elements with AsyncStorage (I'm using react-native-simple-store
a library that works like a wrapper but it's same logic) I want display all items for a key in a list , my code look like this:
constructor(props) {
super(props)
this.state = {
UserInput: "",
}
}
SaveValue = () => {
store.push('Favorites', this.state.UserInput)
Keyboard.dismiss()
};
FetchValue = () => {
store.get('Favorites').then((value) => {
this.setState({
favs: value
});
}).done();
};
Same thing with AsynStorage, it just update the item which is not my goal, I'd like to add a new one
SaveValue = () => {
AsyncStorage.setItem("Favorites", this.state.UserInput);
Keyboard.dismiss()
};
FetchValue = () => {
AsyncStorage.getItem("Favorites").then((value) => {
this.setState({
favs: value
});
}).done();
};
This part is my view where I try to display data, you can also see that I use a text input and two buttons one to save and the other to display an array of items stored
render() {
return (
<View>
<TextInput
onChangeText={(UserInput) => this.setState({UserInput})}
placeholder= "Type something"
value={this.state.UserInput} />
<TouchableOpacity
onPress={this.SaveValue}>
<Text>Save</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={this.FetchValue}>
<Text>Fetch</Text>
</TouchableOpacity>
<Text>{this.state.favs}</Text>
</View>
);
}
At this point I can see only one item, I tried to figure it out and saw that I have to use another method called push but when I changed save by push it throw me an error
Unhandled Promise Rejection : Existing value for key "Favorites" must be of type null or array, revived string.
Thanks!

it will work :)
renderFavorites = () => {
AsyncStorage.getItem("Favorites").then((favs) => {
favs.map((fav) => {
return (<Text> {fav} </Text>);
});
});
}
render() {
return (
<View>
<TextInput
onChangeText={(UserInput) => this.setState({UserInput})}
placeholder= "Type something"
value={this.state.UserInput} />
<TouchableOpacity
onPress={this.SaveValue}>
<Text>Save</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={this.FetchValue}>
<Text>Fetch</Text>
</TouchableOpacity>
{this.renderFavorites()}
</View>
);
}

Solution using JSON:
SaveValue = () => {
const newFavs = [...this.state.favs, this.state.UserInput];
this.setState({ favs: newFavs, UserInput: '' }, () => {
AsyncStorage.setItem("Favorites", JSON.stringify(this.state.favs));
Keyboard.dismiss()
});
};
FetchValue = () => {
AsyncStorage.getItem("Favorites").then((value) => {
this.setState({
favs: JSON.parse(value)
});
}).done();
};

Related

AsyncStorage use boolean from Promise

hi i'm new on react native and i have a issue with asyncStorage. I want to store the cache state in my pdf screen. The cache is a parameter of the source and handle only boolean. I made an onPress which change a state and store it in my localstorage, it works and when i console.log my getItem it shows true or false too it works too. But here is my problem. Now i want to just use the true or the false from this getItem because the parameter cache can handle boolean only. The best i could get on my search was Promise Boolean for my function. So if you could help me it'll be incredible because i really don't know. Thank you a lot and sorry for my English.
Here's my code //
export class Liste extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
navigation : props.navigation,
route: props.route,
selectedIndex : this.selectedIndex,
page : this.page,
numberOfPages : this.numberOfPages,
filePath : [],
cache : false,
};
}
saveCache() {
AsyncStorage.setItem('cache', JSON.stringify(this.state.cache));
console.log(`store ${this.state.cache}`);
}
async getCache () {
const ta = await AsyncStorage.getItem('cache', (value) => {
JSON.parse(value)
})
console.log(ta)
}
navigateBack = () => {
this.state.navigation.goBack();
};
BackAction = () => (
<TopNavigationAction icon={BackIcon} onPress={this.navigateBack}/>
);
render() {
const {files} = this.state.route.params;
const cache = this.state.cache;
const bool = this.getCache();
return (
<>
<TopNavigation style={{ borderWidth: 1 }} title='Mes Articles' alignment='center' accessoryLeft={this.BackAction} />
<ViewPager
selectedIndex={this.state.selectedIndex}
onSelect={ index => this.setState({ selectedIndex: index })}>
{files.map((file, i) =>
<Layout style={styles.tab} level='2'>
<Text>{file.filename}</Text>
<Text>Article: {i + 1} / {files.length} page: {this.state.page} / {this.state.numberOfPages}</Text>
<View>
<TopNavigationAction icon = {emailIcon} onPress={() => Share.open({ title: 'Pdf file', message: `bonjour voici l'article pdf ${file.filename}`, url: `file:///${this.state.filePath[i]}`, subject: `Article Pdf ${file.filename}` })} status='Partager'>
Partager
</TopNavigationAction>
<TopNavigationAction icon = {pin} onPress ={() => this.saveCache(cache === true ? this.setState({cache : false}) : this.setState({cache : true}))} status='Partager'>
Partager
</TopNavigationAction>
<TopNavigationAction icon = {pin} onPress ={() => console.log(this.getCache())} status='Partager'>
Partager
</TopNavigationAction>
</View>
<Pdf
source={{ uri: `http://10.1.0.248/${file.path}/${file.filename}`, cache : bool}}
style={styles.pdf}
enablePaging={true}
onLoadComplete={(numberOfPages, filePath) => {
this.state.filePath.push(filePath);
this.setState({ numberOfPages: numberOfPages });
}}
onPageChanged={(page, numberOfPages) => {
this.setState({ page: page });
}}
/>
</Layout>
)}
</ViewPager>
</>
);
}
}
You can use it like this.
await AsyncStorage.getItem('cache'); returns a JSON stringified value which you could parse and use.
async getCache () {
const ta = await AsyncStorage.getItem('cache');
console.log(JSON.parse(ta))
}
Use it likewise
let ta = await AsyncStorage.getItem('cache');
ta = JSON.parse(ta);

Why is my entered text in react-native SearchBar is getting cleared automatically after some milliseconds?

My entered text in SearchBar is getting cleared automatically after some milliseconds when I start typing into it. It goes back to the placeholder state, what could be the issue?
Here is my code:
return (
<View style={{ marginTop: 30 }}>
<SearchBar
placeholder="Type Here..."
lightTheme
round
onChangeText={this.handleSearch}
/>
</View>
);
};
this is the code for handleSearch method,
handleSearch = (text) => {
const formatQuery = text.toLowerCase();
const data = _.filter(this.state.fullData, (user) => {
return contains(user, formatQuery);
});
this.setState({ query: formatQuery, data }, () => this.makeRemoteRequest());
};
code for makeRemoteRequest():
makeRemoteRequest = _.debounce(() => {
this.setState({ loading: true });
getUsers(20, this.state.query)
.then((users) => {
this.setState({
loading: false,
data: users,
fullData: users,
});
})
.catch((error) => {
this.setState({ error, loading: false });
});
}, 250);
The issue persists even if I remove the debounce method so I think the issue is related to something else.
Also, this is like my 3rd day with react-native development so please excuse any newbie mistakes.
if i am not mistaken, you should add a value prop to the SearchBar, and put a state variable like so :
<SearchBar
placeholder="Type Here..."
lightTheme
round
onChangeText={this.handleSearch}
value={this.state.text} //use deconstructing
/>
And update it before doing your handle search
handleSearch = (text) => {
this.setState({text})
const formatQuery = text.toLowerCase();
const data = _.filter(this.state.fullData, (user) => {
return contains(user, formatQuery);
});
this.setState({ query: formatQuery, data }, () => this.makeRemoteRequest());
};
You should check the SearchBar doc from react-native-elements : https://react-native-elements.github.io/react-native-elements/docs/searchbar.html

React Native: How to change inputfield to selected item

So what I'm trying to do is fetching data from an API (works well), that has this autocomplete function.
Link to example: https://autocomplete.aws.dk/
Link to the guide: https://autocomplete.aws.dk/guide2.html
What is hard for me, is that the guide is HTML, and this is suppose to work in React Native.
So far I made an input field, that can detect when writing minimum two letters will show a list of addresses.
What I want is when the address is clicked, it takes that value and places it in the input field.
Heres my code:
The API fetch:
import React from "react";
import url from "./url";
export default class DawaFetch extends React.Component {
static defaultProps = {
options: {},
minCharacters: 2,
};
state = {
value: "",
suggestions: [],
};
handleChange = ({ target: { value } }) => {
this.setState({ value });
if (this.props.minCharacters <= value.length) this._fetch(value);
};
_fetch = (value) => {
fetch(
url("https://dawa.aws.dk/adresser/autocomplete", {
q: value,
["per_side"]: 100,
...this.props.options,
}),
{
method: "GET",
headers: {
"Accept-Encoding": "gzip, deflate",
},
}
)
.then((response) => response.json())
.then((json) => this.setState({ suggestions: json }))
.catch((err) => console.error("parsing failed", err));
};
render = () =>
this.props.children({ ...this.state, handleChange: this.handleChange });
}
And here is my view:
<DawaFetch>
{({ value, suggestions, handleChange }) => {
console.log(suggestions);
return (
<View>
<CustomInputs
type="text"
value={value}
onChange={handleChange}
/>
{suggestions.map((suggestion) => (
<TouchableOpacity>
<NormalText key={suggestion.adresse.id}>{suggestion.tekst}</NormalText>
</TouchableOpacity>
))}
</View>
);
}}
</DawaFetch>
Tried different solutions by making it a FlatList with renderItem, and making an onPress function, but I could never make it work.
Hopefully someone can guide me in the right direction, I might been overthinking this.
React-Native TextInput onChange value is not available inside the target as it's available in HTML, Remove target from handleChange function like below, also it's not onChange it's onChangeText in TextInput.
handleChange = (value) => {
this.setState({ value });
if (this.props.minCharacters <= value.length) this._fetch(value);
};
You can achieve your desired functionality in a very simple manner.
Add this to your DawaFetch class.
OnItemSelection=(address)=>{
this.setState({value: address})
}
Add this to your render Function
render = () =>
this.props.children({ ...this.state, handleChange: this.handleChange, OnItemSelection: this.OnItemSelection });
}
Then make these changes in your DawaFetch component
<DawaFetch>
{({ value, suggestions, handleChange, OnItemSelection }) => {
console.log(suggestions);
return (
<View>
<CustomInputs
type="text"
value={value}
onChangeText={handleChange}
/>
{suggestions.map((suggestion) => (
<TouchableOpacity onPress={()=> OnItemSelection(suggestion.adresse)}>
<NormalText key={suggestion.adresse.id}>{suggestion.tekst}</NormalText>
</TouchableOpacity>
))}
</View>
);
}}
</DawaFetch>
Edit:Here is Snack having solution
https://snack.expo.io/#waheed25/bad-raisins

How to pass a list with objects from Server to FlatList?

I'm trying to create a FlatList that contains an array with objects that has an array from a server and I can't get it to work.
my error message:
TypeError: Cannot read proparty 'Data' of undefined
I can get it to work with my normal list that's not fetched from a Server. Heres the structure from my working list
[{"DATA":[{"filter_id":"44","filter_name":"filter 1"}, {"filter_id":"45","filter_name":"filter 2"},{"filter_id":"46","filter_name":"filter 3"},{"filter_id":"47","filter_name":"filter 4"},{"filter_id":"48","filter_name":"filter 5"}],"MESSAGE":"DATA FOUND","STATUS":200}]
My server list have the same structure but different values of filter_name and filter_id
here's my code:
constructor(props){
super(props);
this.state = {
data: [],
oldData: [{"DATA":[{"filter_id":"44","filter_name":"filter 1"},{"filter_id":"45","filter_name":"filter 2"},{"filter_id":"46","filter_name":"filter 3"},{"filter_id":"47","filter_name":"filter 4"},{"filter_id":"48","filter_name":"filter 5"}],"MESSAGE":"DATA FOUND","STATUS":200}],
page:1,
status: null,
isLoading: false,
}
}
getData = async () => {
const url = 'api/getFilter.php?page='+this.state.page+'&row_per_page=5';
fetch(url).then((response) => response.json())
.then((responseJson) => {
this.setState({
data:this.state.data.concat(responseJson),
isLoading:false
});
})
}
componentDidMount() {
this.getData();
}
renderRow = ({item}) => {
console.log('item', item);
return (
<View style={styles.item}>
<Text>{item.filter_name}</Text>
</View>
)
}
render() {
console.log('state', this.state.data[0]);
console.log('oldstate', this.state.oldData[0]) // this
return (
<View style={styles.container}>
<FlatList
data={this.state.data[0].DATA}
renderItem={this.renderRow}
keyExtractor={(item, index) => index.toString()}
/>
</View>
);
}
Expo: https://snack.expo.io/#thesvarta/tenacious-sandwich
The issue is that on the initial rendering of your component this.state.data is empty, because we have to wait until getData returns any data. That's why you cannot access this.state.data[0].DATA at the beginning.
The solution is to update your getData function a little bit.
getData = async () => {
const url = 'http://ollenorstrom.se/ollenorstrom.se/avoka/api/getFilter.php?page='+this.state.page+'&row_per_page=5';
fetch(url).then((response) => response.json())
.then((responseJson) => {
// here we save the data, we want to access later.
console.log('responseJson', responseJson[0].DATA);
this.setState({
data:this.state.data.concat(responseJson[0].DATA),
isLoading:false
});
})
}
Now your data is directly stored in this.state.data. We now can simplify your render() function:
<FlatList
data={this.state.data} // simplified, passing an empty array at the beginning is ok
renderItem={this.renderRow}
keyExtractor={(item, index) => index.toString()}
/>
Working example:
https://snack.expo.io/HJ--GFlnN

while using admob in expo i get an error stating "AdmobRewarded.requestAd is not a function"

i am using admob from expo ,while calling showRewarded method its returning an error as follows :
expo.AdmobRewarded.requestAd(function () { return_expo.AdMobRewarded.showAd();})','_expo.AdMobRewarded.requestAd' is undefined)
i tried checking the method and object did not find any problems there
import { Constants , AdMobRewarded } from 'expo'
class QuoteScreen extends React.Component{
state = {
quoteData:[],
value:20
}
incrementCount = () => {
if(this.state.value<=400){
this.setState({
value: this.state.value + 20
},
() => {
this.makeRequest()
}
);
}
else{
Alert.alert(
'Alert',
'No more Quotes to show',
[
{text: 'OK', onPress: () => console.log('OK Pressed')},
],
);
}
}
componentDidMount() {
this.makeRequest()
AdMobRewarded.setTestDeviceID("EMULATOR");
// ALWAYS USE TEST ID for Admob ads
AdMobRewarded.setAdUnitID("ca-app-pub3940256099942544/1712485313");
AdMobRewarded.addEventListener("rewardedVideoDidRewardUser", () =>
console.log("did reward user")
);
AdMobRewarded.addEventListener("rewardedVideoDidLoad", () =>
console.log("videodidload")
);
AdMobRewarded.addEventListener("rewardedVideoDidFailToLoad", () =>
console.log("didfailtoload")
);
AdMobRewarded.addEventListener("rewardedVideoDidOpen", () =>
console.log("video didopen")
);
AdMobRewarded.addEventListener("rewardedVideoDidClose", () =>{
console.log("didclose")
this.incrementCount()
}
);
AdMobRewarded.addEventListener("rewardedVideoWillLeaveApplication", () =>
console.log("will leave application")
);
}
componentWillUnmount() {
AdMobRewarded.removeAllListeners();
}
showRewarded() {
// first - load ads and only then - show
AdMobRewarded.requestAd(() => AdMobRewarded.showAd());
}
makeRequest() {
//calling my api using axios
}
render() {
return (
<View style={styles.viewStyles}>
<View style={styles.statusBarStyle} />
{this.renderdata()}
<ImageBackground
source={require('../../assets/quotebg.png')}
style={styles.bgStyle}>
<FlatList
data = {this.state.quoteData}
renderItem={({item})=> (
<Card
quotes= {item.quote}
/>
)}
keyExtractor={(item) => item.id}
ListHeaderComponent={<Header headerText='Books' />}
onEndReached={this.showRewarded}
onEndThreshold = {0}
/>
</ImageBackground>
</View>
);
}
};
it first loads 20 quotes and as the userr scrolls down flatlists onEndReached gets triggerd which calls the showRewarded method . then it triggers the error. any help is appreciated. Thanks in advance.