Loading Json response to a dropdown in react native - javascript

I'm using a material dropdown in my application
<Dropdown
baseColor='white'
itemColor='white'
label='Select Cluster'
/>
I fetch JSON object like this and it works fine.
fetch('url', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
username : "admin"
})
}).then((response) => response.json())
.then((responseJson) => {
var count = Object.keys(responseJson.message.Obj).length;
for(var i=0;i<count;i++){
console.log(responseJson.message.Obj[i].name) // I need to add
//these names to dropdown
}
})
.catch((error) => {
console.error(error);
});
Now I need to add the responseJson.message.Obj[i].name values to
my dropdown list.

Supposing that you're using react-native-material-dropdown.
Example:
Dropdown component should be rendered as follows:
<Dropdown
baseColor='white'
itemColor='white'
label='Select Cluster'
data={this.state.drop_down_data} // initialise it to []
/>
Request code:
fetch('url', {
...
}).then((response) => response.json())
.then((responseJson) => {
var count = Object.keys(responseJson.message.Obj).length;
let drop_down_data = [];
for(var i=0;i<count;i++){
console.log(responseJson.message.Obj[i].name) // I need to add
drop_down_data.push({ value: responseJson.message.Obj[i].name }); // Create your array of data
}
this.setState({ drop_down_data }); // Set the new state
})
.catch((error) => {
console.error(error);
});
Doc:
React native state management
react-native-material-dropdown

You can achieve this by using react native "state". Create a state then assign it to Dropdown component's data property. Then set responseJson.message.Obj[i].names to the state by using "this.setState()" method.

Find out what is the shape of the data property needed (i.e. Array of Objects) for the <Dropdown /> component you are using
Make fetch calls inside componentDidMount
Treat the state of your component as if it were immutable (do not push directly to this.state. dropdownData)
Here is some sample code using react-native-material-dropdown:
class Example extends React.Component {
// Use constructor to assign initial state
constructor(props) {
this.state = {
dropdownData: []
};
}
componentDidMount() {
fetch('url', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
username : "admin"
})
})
.then((response) => response.json())
.then((responseJson) => {
var count = Object.keys(responseJson.message.Obj).length;
// We need to treat this.state as if it were immutable.
// So first create the data array (tempDataArray)
var tempDataArray = [];
for(var i=0;i<count;i++){
// Note: react-native-material-dropdown takes an Array of Objects
tempDataArray.push({
value: responseJson.message.Obj[i].name
});
}
// Now we modify our dropdownData array from state
this.setState({
dropdownData: tempDataArray
});
})
.catch((error) => {
console.error(error);
});
}
// ...
render() {
return (
// ...
<Dropdown
baseColor='white'
itemColor='white'
label='Select Cluster'
data={this.state.dropdownData} // Use dropdownData for the data prop
/>
// ...
);
}
// ...
}
See: AJAX Requests in React
See: react-native-material-dropdown expected data type

Sample code
import React, { Component } from 'react';
import { AppRegistry, StyleSheet, View, Platform, Picker, ActivityIndicator, Button, Alert} from 'react-native';
export default class AddInventory extends Component {
constructor(props)
{
super(props);
this.state = {
isLoading: true,
PickerValueHolder : ''
}
}
componentDidMount() {
return fetch('https://reactnativecode.000webhostapp.com/FruitsList.php')
.then((response) => response.json())
.then((responseJson) => {
this.setState({
isLoading: false,
dataSource: responseJson
}, function() {
// In this block you can do something with new state.
});
})
.catch((error) => {
console.error(error);
});
}
GetPickerSelectedItemValue=()=>{
Alert.alert(this.state.PickerValueHolder);
}
render() {
if (this.state.isLoading) {
return (
<View style={{flex: 1, paddingTop: 20}}>
<ActivityIndicator />
</View>
);
}
return (
<View style={styles.MainContainer}>
<Picker
selectedValue={this.state.PickerValueHolder}
onValueChange={(itemValue, itemIndex) => this.setState({PickerValueHolder: itemValue})} >
{ this.state.dataSource.map((item, key)=>(
<Picker.Item label={item.fruit_name} value={item.fruit_name} key={key} />)
)}
</Picker>
<Button title="Click Here To Get Picker Selected Item Value" onPress={ this.GetPickerSelectedItemValue } />
</View>
);
}
}
const styles = StyleSheet.create({
MainContainer :{
justifyContent: 'center',
flex:1,
margin: 10
}
});

You could also take an approach like this:
var Somedata = {
"data" : [
{
"baseColor": "white",
"moreData":[
{
"id" : 118,
}
]
},
{
"baseColor": "grey",
"moreData": [
{
"id": 1231231,
}
]
}
]
}
const renderData = someData.data.map((data) => {
return data.moreData.map(brand => {
strColor = data.baseColor;
console.log("Individual Data :" + strColor)
return `${data.baseColor}, ${moreData.id}`
//setState here?
}).join("\n")
}).join("\n")
//setState here?
You now have a few options you could set the state. From your example, you would set the state the state of your list to the data returned from the fetch call once it has been rendered. A quick thing to keep in mind is that react doesn't support asynchronous loading currently. Therefore the data must be rendered as empty, or with some sample data and then updated once the call has been made to whatever you wish to update! Hope this helps a little :)
https://reactjs.org/docs/faq-ajax.html

Related

How to render new data (or GET new) after making a POST request with axios?

I'm using mockapi.io to practice with Axios API call
After I make a POST request, which create a new data, I want to render FlatList with the updated data. I'm thinking of making a new GET request to do that, but I'm not succeeded with it.
I need help
Here is where I call GET request, which already have mock data, and use FlatList to view it
ListScreen.js
class ListScreen extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
}
}
componentDidMount() {
axios.get('insert url')
.then(res => {
this.setState({
data: res && res.data ? res.data : []
})
})
.catch(err => {
console.log('Run into problem')
})
}
render() {
const { data } = this.state;
return (
<View>
<FlatList
data={data}
renderItem={({ item }) => {
return (
<Item
name={item.lastName}
phone={item.phoneNumber}
/>
);
}}
keyExtractor={(item) => item.id}
/>
</View>
And here is where I call POST request
class Create extends Component {
handleSubmitData = (value) => {
console.log('check value: ', value)
axios.post('insert url', {
lastName: `${value.Name}`,
phoneNumber: `${value.Phone}`,
})
.then((response) => {
console.log('here is what you upload: ', response.data)
})
.catch((err) => {
console.log('Run into problem')
})
}
render() {
return (
<CreateForm
handleSubmitData={this.handleSubmitData}
/>
)
}
}
The CreateForm component looks something like this
class CreateForm extends Component {
render() {
const { handleSubmit } = this.props;
return (
<View>
<View>
<Field
name="Name"
component={}
/>
<Field
name="Phone"
component={}
/>
</View>
<View>
<Button
title='Save'
onPress={handleSubmit(this.props.handleSubmitData)}
/>
</View>
</View>
)
This can be done by lifting the state up, from ListScreen to ListScreen's parent.
In ListScreen, take out the state and move into its parent:
this.state = {
data: [],
}
Pass the state down to ListScreen as a prop, also pass the fetch function (see below):
<ListScreen data={this.state.data} fetchListData={this.fetchListData} />
Change ListScreen's render function to access the data from props:
const { data } = this.props;
Move the axios.get() out of ListScreen and into a method on the parent component.
class App extends Component {
fetchListData() {
axios.get('insert url')
.then(res => {
this.setState({
data: res && res.data ? res.data : []
})
})
.catch(err => {
console.log('Run into problem')
})
}
}
In ListScreen, call the function in componentDidMount:
componentDidMount() {
this.props.fetchListData();
}
Alternatively, you could call it in the parent component's componentDidMount(). This might be preferred if you render multiple ListScreen's for example.
Finally, in the Create component, pass the fetch function:
<Create fetchListData={this.fetchListData} />
Call it on successful creation:
.then((response) => {
this.props.fetchListData();
console.log('here is what you upload: ', response.data)
})
As I see in your code, when you are making a POST request it wont display an updated data to your screen because there is no GET request callback upon the success result.
Suggesting you are not using redux , in ListScreen.js you could wrap the GET request into a function and call it in componentDidMount(). It should be look like:
const getData () => {
axios.get('insert url')
.then(res => {
this.setState({
data: res && res.data ? res.data : []
})
})
.catch(err => {
console.log('Run into problem')
})
}
componentDidMount() {
this.getData()
}
Therefore, you need to pass or drill the GET request function into your child component as a props and use it in a POST request callback. The final POST request method should be look like:
handleSubmitData = (value) => {
console.log('check value: ', value)
axios.post('insert url', {
lastName: `${value.Name}`,
phoneNumber: `${value.Phone}`,
})
.then((response) => {
console.log('here is what you upload: ', response.data)
getData()
})
.catch((err) => {
console.log('Run into problem')
})
}
As you can see, after your POST request is finished, it will trigger a GET request to update your parent state, resulting a screen with an updated data. However, you have to make sure to pass your parameters correctly based on how your component structure is.

React - Iterating through state array with no result

I'm fetching JSON data from a local express server using mongoosejs to parse mongoDB queries. I'm getting a "title" and "id" from the server, to generate a sidebar of buttons.
But my issue is that I can't seem to iterate through the state array after fetching the data from the server
The GET request handler on the server:
exports.sidebar = (req, res) => {
Note.find({}, "_id title")
.then(notes => {
console.log(notes);
res.send(notes)
}).catch(err => {
return res.status(404).send({
message: err
})
})
}
My react component. in loadData(), i fetch the data from the API. and in loadSidebarElements() I try to iterate through the state array,
import React, {Component} from "react";
class Sidebar extends Component {
constructor(props) {
super(props);
this.state = {
titleList: [],
idList: []
}
this.loadData = this.loadData.bind(this);
this.loadSidebarElements = this.loadSidebarElements.bind(this);
}
componentDidMount() {
this.loadData();
}
loadData() {
let titles = [];
let ids = [];
const requestOptions = {
method: "GET",
headers: {"Content-Type" : "application/json"},
mode: "cors"
};
fetch("http://127.0.0.1:5000/notes/sidebar", requestOptions)
.then(response => response.json())
.then(data => {
data.map(item => {
titles[titles.length] = String(item.title);
ids[ids.length] = String(item._id);
})
})
this.setState({
titleList: titles,
idList: ids
})
}
loadSidebarElements(){
console.log(this.state.titleList);
this.state.titleList.map(item => {
console.log(item);
})
this.state.titleList.forEach(item => console.log(item));
Object.keys(this.state.titleList).map((key, index) => {
console.log(this.state.titleList[key]);
})
}
render() {
return (
<div className="sidebar" style={{display: "flex", flexDirection:"column", alignItems: "center"}}>
<h1>Sidebar:</h1>
{this.loadSidebarElements()}
</div>
)
}
}
export default Sidebar;
The console output in firefox:
Console log from firefox
I obviously get the data to the server, but I just can't seem to iterate through it...

How do you re-render Flatlist when value of dropdown list changes?

I am fetching an array from the database in Componentdidmount in the state variable this.state.dataSource
componentDidMount(){
fetch("http://docbook.orgfree.com/home.php", {
method: "GET",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
"auth-token": "my token",
},
})
.then((response) => response.json())
.then((responseJson) => {
this.setState({
isLoading: false,
dataSource: responseJson,
});
if (responseJson) {
Alert.alert("Id is" + JSON.stringify(responseJson));
// this.state.dataSource = this.state.dataSource.filter(x => x.Tag === this.state.text);
// console.log(this.state.dataSource[0])
} else if (responseJson.error) {
Alert.alert(responseJson.error);
}
})
.catch((error) => {
console.error(error);
});
}
this.state.Datasource contains an array like:
[
{
description:"kjs",
tag:"beach",
name:"nkslk",
place:"kdlk",
image:"kgmls"
},
{
description:"knsldk",
tag:"club",
name:"nklf",
place:"dlk",
image:"nkxn"
},
]
I have a dropdown list that contains value of different tags in my database like
beach,club,temple,fort,etc
I want to render only those items in my flat list whose tag matches with the tag in my array and when the dropdown value changes I want to re-render my flatlist to the Array elements which has the new tag
My complete source code:
import React, { Component } from "react";
import { Dropdown } from 'react-native-material-dropdown';
import { Button, View, Text, StyleSheet, Image ,Alert,FlatList} from "react-
native";
class explore extends Component {
constructor(props) {
super(props);
this.state = {
tag: '',
isLoading:true,
dataSource:[]
};
}
componentDidMount(){
fetch("http://docbook.orgfree.com/home.php", {
method: "GET",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
"auth-token": "my token",
},
})
.then((response) => response.json())
.then((responseJson) => {
this.setState({
isLoading: false,
dataSource: responseJson,
});
if (responseJson) {
// Alert.alert("Id is" + JSON.stringify(responseJson));
console.log(this.state.dataSource)
} else if (responseJson.error) {
// Alert.alert(responseJson.error);
}
})
.catch((error) => {
console.error(error);
});
}
render() {
const { dataSource, tag } = this.state;
const tagFilter = item => {
if (tag) {
return item.tag === tag;
}
return true;
}
let data = [{
value: 'Church',
}, {
value: 'Beach',
}, {
value: 'Temple',
},{
value:'Waterfall'
},
{
value:'Town'
}];
return (
<View>
<Dropdown
label='TAG'
data={data}
onChangeText={tag => this.setState({ tag })}
/>
<FlatList
data={dataSource.filter(tagFilter)}
ItemSeparatorComponent={this.FlatListItemSeparator}
renderItem={({ item }) => (
<View >
<Text >{item.name}</Text>
<Text >#{item.Tag}</Text>
</View>
)}
keyExatractor={({ name }) => name}
/>
</View>
);
}
}
export default explore;
Save the tags to filter by in state and simple filter your data source inline versus in the onChange callback of the dropdown component. The following destructures tag and dataSource from state, and defines a filter function to be used as array::filter callback. If tag is truthy then apply filter if tags match, otherwise return true to allow item to be passed through, i.e. unfiltered.
this.state = {
text: 'Temple',
isLoading: true,
dataSource: [], // <-- provide defined initial state
tag: '', // <-- tag
};
...
render() {
let data = [{
value: 'Church',
}, {
value: 'Beach',
}, {
value: 'Temple',
}];
const { dataSource, tag } = this.state; // <-- destructure
const tagFilter = item => { // <-- filter callback
if (tag) {
return item.tag.toLowerCase() === tag.toLowerCase(); // <-- compare using lowercase!
}
return true;
}
return (
<View>
<Dropdown
label='TAG'
data={data}
onChangeText={tag => this.setState({ tag })} // <-- save tag to state
/>
<FlatList
data={dataSource.filter(tagFilter)} // <-- filter data
ItemSeparatorComponent={this.FlatListItemSeparator}
renderItem={({ item }) => (
<View >
<Text >{item.name}</Text>
<Text >#{item.Tag}</Text>
</View>
)}
/>
</View>
);
}

React Native post request causes infinite loop when displaying array

I am navigating to this 'History' tab from a side menu in React Native Navigation. Got a username for which I get all the 'bookings' made, but I can see in the warning tab that there are countless requests being made even after the component has been mounted, so there's an infinite loop probably caused by setState. Where should I call getHistory(), as in to make only one request, unless of course the component is reloaded. Thank you!
constructor(props){
super(props);
this.state = {
loggedUser: 'none',
bookingsInfo: []
}
}
getData = async () => {
try {
const value = await AsyncStorage.getItem('loggedUser')
if(value !== null) {
this.setState({
loggedUser: value
})
}
} catch(e) {
console.error(e);
}
}
getHistory() {
fetch('https://porsche.e-twow.uk/reactnative/istoric.php', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
'Cache-Control': 'no-cache, no-store'
},
body: JSON.stringify({
username: this.state.loggedUser
})
})
.then((response) => response.json())
.then((data) => {
this.setState({
bookingsInfo: data
});
})
.catch((error) => {
console.error(error);
});
}
componentDidMount() {
this.getData();
}
render() {
this.getHistory();
return (
<View style={styles.view}>
<ScrollView style={styles.scrollView}>
{
this.getHistory()
}
{
this.state.bookingsInfo ? this.state.bookingsInfo.map((item, index) => {
return (
<View style={styles.mainButton} key={item.id_scooter}>
<Text style={styles.mainButtonText}>Scooter-ul {item.id_scooter}</Text>
<Text style={styles.mainButtonText}>Data start: {item.start}</Text>
<Text style={styles.mainButtonText}>Data final: {item.end}</Text>
</View>
)
}) : null
}
</ScrollView>
<Footer/>
</View>
);
}
}
you are setting state in render.Calling setState here makes your component a contender for producing infinite loops.
place getHistory in componentDidMount .
componentDidMount() {
this.getHistory();
}

Sending multiple duplicate PUT requests

part of this app I'm working on for class is supposed to be scanning the barcode of a book (using the expo XDE barcodescanner component) and then sending the scanned barcode to a database that another group in my class is handling. My issue right now is that every time I do a scan, I see in my console that I'm sending multiple duplicate PUT requests. I think the problem is that the expo barcodescanner doesn't just scan once and then stop, but keeps on scanning, and each time it scans, my state is "updated" and component is re-rendered. Does anyone have any suggestions on how I can modify my code to make sure that I'm not re-rendering over and over again with the same data? I've included the relevant code below. Note: some of the data is hard-coded for testing purposes. Thank you!
class SecondScreen extends Component {
constructor(props) {
super(props);
this.state= {
results: []
}
this.fetchData = this.fetchData.bind(this);
}
fetchData(URL) {
return fetch(URL)
.then((response) => response.json())
.then((responseData) => {
return responseData
})
.catch((error) => {
console.error(error)
})
}
_handleBarCodeRead = data => {
let isbn = data.data;
let URL = 'https://www.googleapis.com/books/v1/volumes?q=isbn:' +
isbn;
this.fetchData(URL).then(bookResult => {
this.setState({ results: bookResult }
fetch('https://p0kvnd5htd.execute-api.us-east-
2.amazonaws.com/test/return', {
method: 'PUT',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
'libraryName': 'lib1', //libName
'bookBarcode': '18263' //isbn
}),
})
.then((response) => {
if (response.status == 400) {
console.log(response.status)
alert('Return unsuccessful, please try again.');
}
else {
console.log(response.status)
this.props.navigation.navigate('ThirdPage', { title:
this.state.results.items[0].volumeInfo.title });
}
})
})
}
render() {
return (
<BarCodeScanner
onBarCodeRead={this._handleBarCodeRead}
style={[StyleSheet.absoluteFill, styles.container]}
>
<View style={styles.layerTop} />
<View style={styles.layerCenter}>
<View style={styles.layerLeft} />
<View style={styles.focused} />
<View style={styles.layerRight} />
</View>
<View style={styles.layerBottom} />
</BarCodeScanner>
);
}
}
Easy fix would be to use lodash's debounce function:
command line: npm i lodash
top of javascript:
import _ from 'lodash'
wrap _handleBarCodeRead in debounce, this will prevent _handleBarCodeRead from being called multiple times for 3 seconds after the last call:
_debouncedHandleBarCodeRead = _.debounce((data) =>{ this._handleBarCodeRead(data) }, 3000, {leading: true, trailing: false});
change BarCodeScanner to used the debounced method:
<BarCodeScanner onBarCodeRead={this._debouncedHandleBarCodeRead} >

Categories