In my application, I'm trying to fetch a data from my api. I've already tried to fetch data in my other modules and they're all working fine, but here it's not.
In here I'am trying to fetch a single object/data in my api.
Here's my code
Category.js
export default class Category extends Component {
constructor(props){
super(props)
this.state = {
data: [],
orderDet: '',
};
}
fetchDataOrderNo = async () => {
const response = await fetch("http://192.168.254.105:3308/OrderNo/order_no")
const json = await response.json()
this.setState({ orderDet: json })
}
componentDidMount() {
this.fetchDataOrderNo();
}
render() {
return (
<View>
<View style={{flexDirection: 'row'}}>
<Text>Table No: { this.state.orderDet }</Text>
</View>
</View>
)
}
}
You are getting an array as response to your request. You have to access the first object in the array, and get the order_no key:
fetchDataOrderNo = async () => {
const response = await fetch("http://192.168.254.105:3308/OrderNo/order_no")
const json = await response.json()
this.setState({ orderDet: json[0].order_no })
}
Related
I am learning react.
I have a simple react app sample that :
Fetch users
Once users are fetched, show their name on a Card
What I'd like to do is to expand this sample. Instead of using a simple list of users, I'd like to use a list of pokemons. What I try to do is :
Fetch the list of pokemon and add in state.pokemons
Show the Card with the pokemon name from state.pokemons
From that list, get the URL to fetch the detail of the given pokemon and add in state.pokemonsDetails
From the state.pokemonsDetails, update the Cards list to show the image of the pokemon.
My problem is: I don't even know how to re-render the Cards list after a second fetch.
My question is: How to update the Cards list after the second fetch?
See my code below:
import React from "react";
import CardList from "../components/CardList";
import SearchBox from "../components/SearchBox"
import Scroll from "../components/Scroll"
import './App.css';
class App extends React.Component{
constructor(){
super();
this.state = {
pokemons:[],
pokemonsDetails:[],
searchfield: ''
}
}
getPokemons = async function(){
const response = await fetch('https://pokeapi.co/api/v2/pokemon/?offset=0&limit=20');
const data = await response.json();
this.setState({pokemons:data.results})
}
getPokemonDetails = async function(url){
//fetch function returns a Promise
const response = await fetch(url);
const data = await response.json();
//console.log('getPokemonDetails', data);
this.setState({pokemonsDetails:data});
}
componentDidMount(){
this.getPokemons();
}
onSearchChange = (event) => {
this.setState({searchfield: event.target.value})
}
render(){
const {pokemons, pokemonsDetails, searchfield} = this.state;
if(pokemons.length === 0){
console.log('Loading...');
return <h1>Loading....</h1>
}else if (pokemonsDetails.length === 0){
console.log('Loading details...');
pokemons.map(pokemon => {
return this.getPokemonDetails(pokemon.url);
});
return <h1>Loading details....</h1>
}else{
return(
<div>
<h1>Pokedex</h1>
<SearchBox searchChange={this.onSearchChange}/>
<Scroll>
<CardList pokemons={pokemons}/>
</Scroll>
</div>
);
}
}
}
export default App;
Some remarks :
I can see a problem where my Cards list is first created with state.pokemons, then, I would need to update Cards list with state.pokemonsDetails. The array is not the same.
Second problem, I don't even know how to call the render function after state.pokemonsDetails is filled with the fetch. I set the state, but it looks like render is not called every time
More a question than a remark. The way I update my state in getPokemonDetails might be incorrect. I keep only one detail for one given pokemon. How to keep a list of details? Should I use something else than setState to expand pokemonsDetails array?
You can combine 2 API calls before pokemons state update that would help you to control UI re-renderings better
You can try the below approach with some comments
Side note that I removed pokemonDetails state, so you won't see the loading elements for pokemonDetails as well
import React from "react";
import CardList from "../components/CardList";
import SearchBox from "../components/SearchBox";
import Scroll from "../components/Scroll";
import "./App.css";
class App extends React.Component {
constructor() {
super();
this.state = {
pokemons: [],
searchfield: ""
};
}
getPokemons = async function () {
const response = await fetch(
"https://pokeapi.co/api/v2/pokemon/?offset=0&limit=20"
);
const data = await response.json();
//try to get all pokemon details at once with fetched URLs
const pokemonDetails = await Promise.all(
data.results.map((result) => this.getPokemonDetails(result.url))
);
//map the first and second API response data by names
const mappedPokemons = pokemonDetails.map((pokemon) => {
const pokemonDetail = pokemonDetails.find(
(details) => details.name === pokemon.name
);
return { ...pokemon, ...pokemonDetail };
});
//use mapped pokemons for UI display
this.setState({ pokemons: mappedPokemons });
};
getPokemonDetails = async function (url) {
return fetch(url).then((response) => response.json());
};
componentDidMount() {
this.getPokemons();
}
onSearchChange = (event) => {
this.setState({ searchfield: event.target.value });
};
render() {
const { pokemons, searchfield } = this.state;
if (pokemons.length === 0) {
return <h1>Loading....</h1>;
} else {
return (
<div>
<h1>Pokedex</h1>
<SearchBox searchChange={this.onSearchChange} />
<Scroll>
<CardList pokemons={pokemons} />
</Scroll>
</div>
);
}
}
}
export default App;
Sandbox
If you want to update pokemon details gradually, you can try the below approach
import React from "react";
import CardList from "../components/CardList";
import SearchBox from "../components/SearchBox";
import Scroll from "../components/Scroll";
import "./App.css";
class App extends React.Component {
constructor() {
super();
this.state = {
pokemons: [],
searchfield: ""
};
}
getPokemons = async function () {
const response = await fetch(
"https://pokeapi.co/api/v2/pokemon/?offset=0&limit=20"
);
const data = await response.json();
this.setState({ pokemons: data.results });
for (const { url } of data.results) {
this.getPokemonDetails(url).then((pokemonDetails) => {
this.setState((prevState) => ({
pokemons: prevState.pokemons.map((pokemon) =>
pokemon.name === pokemonDetails.name
? { ...pokemon, ...pokemonDetails }
: pokemon
)
}));
});
}
};
getPokemonDetails = async function (url) {
return fetch(url).then((response) => response.json());
};
componentDidMount() {
this.getPokemons();
}
onSearchChange = (event) => {
this.setState({ searchfield: event.target.value });
};
render() {
const { pokemons, searchfield } = this.state;
if (pokemons.length === 0) {
return <h1>Loading....</h1>;
} else {
return (
<div>
<h1>Pokedex</h1>
<SearchBox searchChange={this.onSearchChange} />
<Scroll>
<CardList pokemons={pokemons} />
</Scroll>
</div>
);
}
}
}
export default App;
Sandbox
Side note that this approach may cause the performance issue because it will keep hitting API for fetching pokemon details multiple times and updating on the same state for UI re-rendering
I am working on a web dev using ReactJs, NodeJs and Mysql. I got problem in displaying fetching data using axios.
here is my API :
app.get('/enduser',(req, res) => {
let ENDUSER_QUERY = "SELECT * FROM enduser_tbl";
let query = dbConn.query(ENDUSER_QUERY, (err, results) => {
if(err) {
console.log(err)
} else {
return res.status(200).json({"status": 200, "err" : null, "response": results});
}
});
});
and I call the API in my reactjs page component
class ProjectList extends Component {
constructor(props){
super(props);
this.state = {
enduser_tbl : []
}
}
async componentDidMount() {
const Url = "http://localhost:4000/enduser"
await axios.get(Url)
.then( enduser_tbl => {
console.log(enduser_tbl.data)
this.setState({
enduser_tbl : enduser_tbl.data
})
})
}
render() {
const enduser_tbl = this.state;
return (
<Container>
{ enduser_tbl.map ((enduser, project_id) =>
<ListGroup>
<ListGroup.Item key={project_id}> {enduser.project_type} </ListGroup.Item>
</ListGroup>
)}
</Container>
)
}
}
export default ProjectList
I got no error in my terminal but many problem appears in Chrome. here is the response from chrome
Error in destructuring, missed curly brace const { enduser_tbl }
render() {
const { enduser_tbl = [] } = this.state;
return (
<Container>
{ enduser_tbl.map ((enduser, project_id) =>
<ListGroup>
<ListGroup.Item key={project_id}> {enduser.project_type} </ListGroup.Item>
</ListGroup>
)}
</Container>
)
}
For safe side:
const Url = "http://localhost:4000/enduser"
await axios.get(Url)
.then( { data: enduser_tbl = [] } => {
console.log(data)
this.setState({
enduser_tbl
})
})
enduser_tbl.data will be undefined,
it will be either enduser_tbl.response or enduser_tbl which you are setting in your state.
this.setState({
enduser_tbl : enduser_tbl.response
})
Two things, you are not getting the correct piece of the state in your render function. You also need to deal with that both the API call and setState are async, so you can't rely on it being defined when your component is rendered.
You can do it like this:
const enduser_tbl = this.state.enduser_tbl || [];
return (
<Container>
{ enduser_tbl.map ((enduser, project_id) =>
<ListGroup>
<ListGroup.Item key={project_id}> {enduser.project_type} </ListGroup.Item>
</ListGroup>
)}
</Container>
)
I think that you are trying to map an object because this.state is an object. Try changing the code as given below.
class ProjectList extends Component {
constructor(props){
super(props);
this.state = {
enduser_tbl : []
}
}
async componentDidMount() {
const Url = "http://localhost:4000/enduser"
await axios.get(Url)
.then( enduser_tbl => {
console.log(enduser_tbl.data)
this.setState({
enduser_tbl : enduser_tbl.data
})
})
}
render() {
const enduser_tbl = this.state.enduser_tbl;
return (
<Container>
{ enduser_tbl.map ((enduser, project_id) =>
<ListGroup>
<ListGroup.Item key={project_id}> {enduser.project_type} </ListGroup.Item>
</ListGroup>
)}
</Container>
)
}
}
This error rises because you are not passing array to map function please add below code and let me know is it work for you are not
componentDidMount = async (e) =>{
const url = "http://localhost:4000/enduser"
try{
const res = await axios.get(url)
this.setState({
enduser_tbl :res.data
})
}
ctach(ex){
console.log(ex)
}
}
First, check your API response it might look like this
{
"status": 200,
"response": [{...}, {...}]
}
then when receiving your data make sure you've set it to the state properly
e.g:
await axios.get(Url)
.then(enduser_tbl => {
this.setState({
enduser_tbl: enduser_tbl.response
})
})
finally, make sure that you've destructured it properly just like xdeepkav said e.g
const { enduser_tbl } = this.state;
The error the you're encountering is because enduser_tbl can't be read as mappable data/array. To make it clear here's an example of your error
I keep trying to call an image location using stored data on firebase it seems to work fine on my other component, however, I keep getting this error on this component when switching to the page on my app, even the console logging of the data works fine. Any help would be great thanks!
export class Dog extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
};
}
componentDidMount = async () => {
await firebase
.database()
.ref("pets")
.on("value", snapshot => {
this.setState({ data: [].concat.apply([], snapshot.val()) });
});
};
render() {
return (
<View>
{this.state.data.map((obj, index) => {
console.log(obj.image);
return (
<View key={index}>
<Image source={require(obj.image)} />
</View>
);
})}
</View>
);
}
}
export default Dog;
What i want to make is a bookmark list, but i don't know how i can save an array of items in AsyncStorage, my skills are basic on react.
I just need the function to save posts (post image, title and id) when i press the button "Save to bookmark".
export default class PostDetails extends Component {
constructor(props) {
super(props);
const {params} = props.navigation.state;
this.state = {
item: params.item
};
}
render() {
const {item} = this.state;
return (
<Image source={{uri: item.post_image}}/>
<Text> {item.post_id} </Text>
<Text> {item.post_title} </Text>
<Button>
<Text> Save to Bookmark </Text>
</Button>
);
}
}
I think you want to use JSON.stringify(arrayToSave); see the docs for JSON.stringify(). It will convert the array to a JSON string that can be saved to AsyncStorage and then can be retrieved at a later stage.
const saveBookmarks = async (bookmarksArray) => {
try {
const bookmarksString = JSON.stringify(bookmarksArray);
await AsyncStorage.setItem('#MyStore:bookmarks',bookmarksString);
} catch (error) {
// Error saving data
}
};
<Button onClick={() => saveBookmarks(yourArray)} />
To retrieve it you can use const theSavedArray = JSON.parse(stringFromAsyncStorage); the docs for JSON.parse()
try {
const bookmarksString = await AsyncStorage.getItem('#MyStore:bookmarks');
if (bookmarksString !== null){
// We have data!!
const bookmarksArray = JSON.parse(bookmarksString);
}
} catch (error) {
// Error retrieving data
}
I am breaking down a larger post into smaller questions. Please understand I never used Promise before and that I am new to React-Native too. It would be great to get feedback and recommendations on how to setup API calls and handle the data. Thank you in advance.
How can I dynamically create URLs for API requests? Here's what I am trying to achieve:
Pseudocode
Child
Retrieve two variables
Use these two variables to build an URL
Trigger the first Promise and resolve
Retrieve another two variables
Use these two variables to build a new an URL
Trigger the second Promise and resolve
Gather the data from both promises and pass to parent
Parent
Retrieve data from Child
Get data from the first Promise and set to a state
Get data from the second Promise and set to another state
APIservice.js
Child
class APIservice {
_getStopPoint = (endpoint) => {
return new Promise(function(resolve, reject) {
fetch(endpoint)
.then((response) => response.json())
.then((data) => {
console.log("APIservice StopPoint", data)
resolve(data);
});
});
};
};
module.exports = new APIservice
List.js
Parent
As you can see, the way I setup the endpoint is lame. It's not ideal as the URL is the same. I want to structure something that can receive two variables and build the URL on the go. Something like https://api.tfl.gov.uk/Line/${routeid}/Arrivals/${stationid}.
If I manage that, how can I pass the API call to the APIservice having only one endpoint that dynamically will change based on the two variables it receives? I am not sure how to differentiate the call in the Promise.all having only "one" URL.
let APIservice = require('./APIservice')
let endpoint = 'https://api.tfl.gov.uk/Line/55/Arrivals/490004936E'
let endpoint1 = 'https://api.tfl.gov.uk/Line/Northern/Arrivals/940GZZLUODS'
export class List extends Component {
constructor(props) {
super(props);
this.state = {
bus: null,
tube: null,
}
};
componentWillMount() {
let loadData = (endPoint) => {
Promise.all([
APIservice._getStopPoint(endpoint),
APIservice._getStopPoint(endpoint1),
])
.then((data) => {
// Name for better identification
const listBus = data[0]
const listTube = data[1]
this.setState({
bus: listBus,
tube: listTube
}, () => {
console.log("bus", this.state.bus, "tube", this.state.tube)
});
})
.catch((error) => {
console.log(error)
})
}
loadData(endpoint);
loadData(endpoint1);
}
render() {
return(
<View>
<FlatList
data={this.state.bus}
renderItem={({item}) => (
<Text>{item.timeToStation}</ Text>
)}
keyExtractor={item => item.id}
/>
<FlatList
data={this.state.tube}
renderItem={({item}) => (
<Text>{item.timeToStation}</ Text>
)}
keyExtractor={item => item.id}
/>
</ View>
);
}
};
It is pretty easy to implement what you are saying once you understand how this works.
You are using fetch for your API calls which returns a Promise upon use. The pseudo-code for your use case would be something like this:
class APIService {
static fetchFirst(cb) {
fetch('FIRST_URL')
.then(resp => {
try {
resp = JSON.parse(resp._bodyText);
cb(resp);
} catch(e) {
cb(e);
}
})
.catch(e => cb(e));
}
static fetchSecond(routeid, stationid, cb) {
fetch(`https://api.tfl.gov.uk/Line/${routeid}/Arrivals/${stationid}`)
.then(resp => {
try {
resp = JSON.parse(resp._bodyText);
cb(resp);
} catch(e) {
cb(e);
}
})
.catch(e => cb(e));
}
}
module.exports = APIService;
Include this in your parent component and use it as follows:
let APIService = require('./APIService')
export class List extends Component {
constructor(props) {
super(props);
this.state = {
bus: null,
tube: null,
}
};
componentWillMount() {
APIService.fetchFirst((resp1) => {
APIService.fetchSecond(resp1.routeid, resp1.stationid, (resp2) => {
this.setState({
tube: resp2
});
});
});
}
render() {
return(
<View>
<FlatList
data={this.state.bus}
renderItem={({item}) => (
<Text>{item.timeToStation}</ Text>
)}
keyExtractor={item => item.id}
/>
<FlatList
data={this.state.tube}
renderItem={({item}) => (
<Text>{item.timeToStation}</ Text>
)}
keyExtractor={item => item.id}
/>
</ View>
);
}
};
I haven't checked the errors on the callback function, please see that the errors are handled when you use this.