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
Related
im looking for a bit of guideness here, im working on a RN app with redux and everytime i enter a new screen on the app, must likely i have a "callinitialData" function inside my useEffect(), using axios to fetch api data to be dispatch() to the redux state.
Everything works but whenever i jump from screen to screen to fast, sometimes i get a 429 error of to many request, so i just setup the redux-persist hoping that would help reduce the amount of request,in my mind thinking that if my api data is equal to my local data, that request wouldnt be necessary to be made.
However it stays the same so i was thinking what would be the best aproach here, on login try to fetch all the data at once > store it at asyncstorage and redux, and fetch that on each screen ?
how would i be able then, if i fetch all the data on login, receive the new data sets from the api in real time?
App functionality -
Edit Profile (img, pass, email, name)
Data Forms (requeast X, submit data, edit forms)
Chat by contacts / create Group chat
Code Example
const ChatScreen = ({ auth: { user }, getChatContacts, chat: { contacts }, navigation }) => {
useEffect(() => {
getChatContacts();
}, []);
const onChatUser = async (_id, name, roomID) => {
console.log(_id, name, roomID, contacts.payload.clone)
navigation.navigate( "Message", {
_id, name, chatRoomId: roomID, allUsers: contacts.payload.clone
});
}
const renderItem = ({ item , index } ) => {
let userName = "";
item.users.map((users, index) => {
let idToCheck = users.toString() !== user._id.toString() ? users : false;
if (idToCheck) {
let getOneUser = contacts.payload.clone.find(x => x._id === idToCheck);
userName += "" + getOneUser.name + ", ";
}
})
return (<TouchableOpacity key={item._id} onPress={() => onChatUser(item._id, item.name, item.roomID)}>
<View style={styles.chatContainer}>
<FontAwesome name="user-circle-o" size={50} color="#000000"/>
<Text style={styles.chatTitle}>{ ((userName).length > 32) ?
(((userName).substring(0,32-3)) + '...') :
userName }</Text>
<FontAwesome name="angle-right" size={25} color="#000000"/>
</View>
</TouchableOpacity>)
};
return (
<SafeAreaView style={styles.container}>
<TextInput
autoCapitalize="none"
autoCorrect={false}
clearButtonMode="always"
placeholder="Search friend"
style={styles.chatsearch}
/>
{contacts ?
(<FlatList
data={contacts.payload.allContact}
renderItem={(item, index) => renderItem(item, index)}
keyExtractor={item => item.id}
style={styles.FlatListContainer}
/>) : (<Text style={styles.FlatListContainer}></Text>)
}
</SafeAreaView>
);
}
const styles = StyleSheet.create({});
ChatScreen.propTypes = {
isAuthenticated: PropTypes.bool,
auth: PropTypes.object,
};
const mapStateProps = state => ({
auth: state.auth,
chat: state.chat
});
export default connect(mapStateProps, {getChatContacts} )(ChatScreen);
Redux Action
export const getChatContacts = () => async dispatch => {
const config = {
header: {
"Content-Type": "application/json"
}
}
try {
const res = await axios.get(API_LINK +"/users/getChatContacts",);
dispatch({
type: GET_CONTACT_CHAT,
payload: res.data
});
} catch (err){
console.log(err)
dispatch({
type: ERROR_FAMILY_PARENT,
payload: { msg: err.response, status: err.response}
});
}
};
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
I am using Zomato API to get the list of restaurants, data is in the form of the array which has object restaurants in which there are different restaurant objects and then their name and other details.
It's my first time using a flat List and I am not able to display this data.
Goal: Use the search bar to display the list of restaurants in the city using flatlist.
import React, { Component } from 'react';
import { View, Text } from 'react-native';
import { SearchBar } from 'react-native-elements';
import { FlatList } from 'react-native-gesture-handler';
class Main extends Component {
constructor(props) {
super(props);
this.state = { search: '', data: [] };
}
componentDidMount() {
fetch('https://developers.zomato.com/api/v2.1/search', {
method: 'GET',
headers: {
'user-key': '999999999999999999999999999'
},
params: JSON.stringify({
entity_type: 'city',
q: {this.state.search}
}),
}).then((response) => response.json())
.then((responseJson) => { return this.setState({ data: responseJson.restaurants }) })
.catch((error) => { console.warn(error) }
);
}
render() {
let data = this.state.data;
let name = data.map(p => p.restaurant.name)
console.warn("Check Data", name)
return (
<View>
<SearchBar
round
searchIcon={{ size: 24 }}
placeholder='Search'
onChangeText={search => { this.setState({ search }) }}
value={this.state.search}
/>
//Using this method to display the data if any
{name.length != 0 ?
<FlatList
data={name}
keyExtractor={(x, i) => x + i}
renderItem={({ name }) => <Text>{name}-DATA</Text>}
/>
: <View style={{height:"100%", width:"100%", alignItems:"center",
justifyContent:"center"}}>
<Text>No data found</Text>
</View>
}
</View>
);
}
}
export default Main;
Maybe the way I declared state is wrong, or maybe the way I'm storing the data in the state is wrong.
I got the names of the restaurant in the console.warn successfully.
Without your users-key I can't surely understand what is your api results.
Here
import React, { Component } from 'react';
import { View, Text } from 'react-native';
import { SearchBar } from 'react-native-elements';
import { FlatList } from 'react-native-gesture-handler';
class Main extends Component {
constructor(props) {
super(props);
this.state = { search: '', data: [] };
}
componentDidMount() {
fetch('http://phplaravel-325095-1114213.cloudwaysapps.com/api/shop/shop/', {
method: 'GET',
}).then((response) => response.json())
.then((responseJson) => { return this.setState({ data: responseJson }) })
.catch((error) => { alert(error) }
);
}
render() {
let data = this.state.data;
return (
<View>
<SearchBar
round
searchIcon={{ size: 24 }}
placeholder='Search'
onChangeText={search => { this.setState({ search }) }}
value={this.state.search}
/>
{data.length != 0 ?
<FlatList
data={data}
keyExtractor={(x, i) => x + i}
renderItem={({ item }) => <Text>{item.name}-DATA</Text>}
/>
: <View style={{height:"100%", width:"100%", alignItems:"center",
justifyContent:"center"}}>
<Text>No data found</Text>
</View>
}
</View>
);
}
}
export default Main;
This is a working code with another api call just add your api call instead on mine.. This is working properly. I guess you are just messing with your data.
Try replacing the renderItem from FlatList to
renderItem={({ item }) => <Text>{item}-DATA</Text>}
Also, replace the condition to use double equals like name.length !== 0
Check the url link properly.
https://developers.zomato.com/api/v2.1/search/999999999999999999999999999
Just check it on web browser it is showing message : The requested url was not found
It means we are not getting any data from this URL.
That why we are not able to map any data.
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();
};
I have been unable to solve this so far, I hope someone from here can help me :)
The situation is as following:
I load a list of users from a department using an API which returns it as JSON (this works).
For each user in the JSON file, I have to fetch another JSON file containing sensor data (this does not work).
I am in fact capable of getting the JSON data from the getStatus() function, but it is not correct. Because, when the FlatList is rendered, the data still has not been fetched, but on refresh, calling fetchUsers(), the sensor data shows up, but it shows the same sensor data on all users and not their own specific sensor data.
At the moment I have made it spit out what ever JSON it receives in a Text element in order to see what was returned...
I am not allowed to upload pictures here, but I have made an album on Imgur which contains images of the application to help explain the issue: https://imgur.com/a/5XLpR
export default class SensorData extends Component {
constructor(props) {
super(props);
this.stats = null;
this.state = {
isLoading: true,
error: false,
userDataSource: [],
refreshing: false,
time: 30,
navigation: props.navigation,
};
}
componentDidMount() {
this.fetchUsers();
}
fetchUsers(){
return fetch('http://url-to-the-json/json/patients.json')
.then((response) => response.json())
.then((response) => {
this.setState({
isLoading: false,
error: false,
userDataSource: response,
refreshing: false,
time: 30,
}, function () {
});
})
.catch((error) => {
this.setState({
isLoading: false,
error: true
})
});
}
getStatus(patientId) {
fetch("http://url-to-the-json/json/status/" + patientId + ".json")
.then((response) => response.json())
.then((responseJson) => {
this.stats = responseJson;
})
.catch((error) => {
Alert.alert("Error");
});
return this.stats;
};
renderItem(item, index) {
var x = index % 2 === 0;
status = this.getStatus(item.patientId);
return (
<View style={x ? styles.rowEven : styles.rowOdd}>
<TouchableOpacity style={styles.rowName} onPress={this.handlePress.bind(this, item)}>
<Text style={styles.rowNameText}>{item.firstname} {item.lastname}</Text>
</TouchableOpacity>
<Text>{JSON.stringify(status)}</Text>
</View>
)
}
render() {
return (
<View>
<View style={styles.reloadTimer}>
<Text style={styles.timerText}>Reloading in {this.state.time} seconds.</Text>
</View>
<View style={styles.listView}>
<FlatList
data={this.state.userDataSource}
renderItem={({ item, index }) => this.renderItem(item, index)}
keyExtractor={item => item.patientId}
refreshing={this.state.refreshing}
onRefresh={this.handleRefresh}
ItemSeparatorComponent={this.renderSeparator}
/>
</View>
</View>
);
}
}
The variable status is the one in question.
I hope I formulated the question in an understandable manner.
The users JSON file looks like this:
{
"patientId": "ec276ca9-f9ab-429b-b34e-23fcf448d714",
"firstname": "Julie",
"lastname": "Nielsen",
"birthDate": "1930-01-01T00:00:00Z",
"departmentId": "709f59ae-67fe-447c-bed3-7b5912703861",
"patientNumber": null,
"entryDate": null,
"dischargeDate": null,
"editedOn": null,
"editedBy": null
}
The sensor data JSON file looks like this:
{
"status": {
"in_bed": false,
"in_room": true,
"clean_diaper": true
}
}
You need to store the result of getStatus in component state like you do with the patients JSON. Assuming you have a "stats" object in your state that maps a patient to their stats, you can do:
getStatus(patientId) {
fetch("http://url-to-the-json/json/status/" + patientId + ".json")
.then((response) => response.json())
.then((responseJson) => {
let stats = Object.assign({}, this.state.stats);
stats[patientId] = responseJson;
this.setState({ stats: stats });
})
.catch((error) => {
Alert.alert("Error");
});
};
Then, in the renderItem function, you can render the stats from state, or render a placeholder text if the stats haven't been loaded yet.
Also, you should not make network requests inside a render function, since they can be called quite often and multiple times for the same component. Instead, you should look into the FlatList API and the callbacks for changes in visibility.
Hope this helps...