I wanna display the data that I have in my state in a map I managed to get both latitude and longitude in my state the problem is whenever try to map trough the state I always get it's not a function error here's parts of my code and the data that I have in the state:
const [countriesData, setCountriesData] = useState({});
useEffect(() => {
const fetchAPI = async () => {
setCountriesData(await fetchDataCountries());
};
fetchAPI();
}, [setCountriesData]);
console.log(countriesData);
and mapping through it like this:
{countriesData.map((data)=>(
<Marker latitude={data.countriesData.latitude}
longitude={data.countriesData.longitude}/>
))}
the fetch api function:
export const fetchDataCountries = async () => {
try {
const data = await axios.get(url);
const newData = data.data;
const modfiedData = newData.map((countryData) => {
return countryData.coordinates;
});
return modfiedData;
} catch (error) {
console.log(error);
}
};
For your map to work you need to convert yourstate to an array, now it is an object.
After that, it could look something like this:
{countriesData.map((country) => (
<Marker
latitude={country.latitude}
longitude={country.longitude}
/>
))}
or
{countriesData.map(({ latitude, longitude }) => (
<Marker
latitude={latitude}
longitude={longitude}
/>
))}
But for a more accurate answer, it would be nice to have an example of the contents of your state.
You're initializing the countriesData with an empty object, then you're fetching a API data.
Assuming the response is an array of objects like this:
[
{ latitude: 38.8451, longitude: -37.0294 }
{ ... // other coordinates }
]
Change the initial state to be an empty array like this:
const [countriesData, setCountriesData] = useState([]);
Also, update this block:
{countriesData.map((data) => <Marker latitude={data.latitude} longitude={data.longitude} />)}
Explanation: the map() method only works with Arrays [] and not with Objects {}, therefore when the component was mounted/initialized for the first time, the map method was executed against an object, thus throwing the map is not a function error.
Related
To start with I'm a beginner. Any help would be appreciated.
So I'm getting my data from mongoDB atlas using node+express API. I'm successfull at getting the array to show up in console log using following code.
const [product, setProduct] = useState();
const url = "http://localhost:5000/api/items";
useEffect(() => {
axios.get(url).then((res) => {
setProduct(res.data);
// setProduct(
// JSON.stringify({
// title: setProduct.title,
// price: setProduct.price,
// image: setProduct.image,
// details: setProduct.details,
// })
// );
})
}, [url])
console.log(product)
The console log displays the array properly as collection named 'items' with content of arrays. As you can see I tried to stringify the response as the response returns JSON but again I didn't know how to map Following is the code where I tried to map the contents like id, name etc as props to component.
<div>
{product.map((product) => {
<Product name={product.title} />
})}
</div>
When I do this I get error that the map is not a function. I don't know what I'm doing wrong here. I know I'm supposed to use redux or reducer/context here but I want to get this to work before updating it with those.
[![Response from res.data][1]][1]
[1]: https://i.stack.imgur.com/auxvl.png
you didnt get yours products.
As we can see from screenshot
res.data equal to object with one property items:
res.data= {items: []}
and we need to take/access to these items
use this: setProducts(res?.data?.items || [])
const [products, setProducts] = useState();
useEffect(() => {
axios.get(url).then((res) => {
setProducts(res?.data?.items || []);
})
}, [url])
<div>
{products?.map((product) => {
<Product name={product.title} />
})}
</div>
On the first render the value for types will be undefined ( on sync code execution ), try using it as
<div>
{product?.map((product) => {
<Product name={product.name} />
})}
</div>
? will make sure to run map once value for types is there ( also make sure it is mappable ( is an array ))
I am pretty much familiar with the async await but with back end nodejs. But there is a scenario came across to me where I have to use it on front end.
I am getting array of objects and in that objects I am getting lat lng of the places. Now using react-geocode I can get the place name for a single lat lng but I want to use that inside the map function to get the places names. SO as we know it async call I have to use async await over there.
Here is the code
import Geocode from "react-geocode";
render = async() => {
const {
phase,
getCompanyUserRidesData
} = this.props
return (
<div>
<tbody>
await Promise.all(_.get(this.props, 'getCompanyUserRidesData', []).map(async(userRides,index) => {
const address = await Geocode.fromLatLng(22.685131,75.873468)
console.log(address.results[0].formatted_address)
return (
<tr key={index}>
<td>
{address.results[0].formatted_address}
</td>
<td>Goa</td>
<td>asdsad</td>
<td>{_.get(userRides,'driverId.email', '')}</td>
<td>{_.get(userRides,'driverId.mobile', '')}</td>
</tr>
)
}))
</tbody>
</div>
)
}
But when I use async with the map function here it doesn't return anything. Can anyone please help me where I going wrong?
You should always separate concerns like fetching data from concerns like displaying it. Here there's a parent component that fetches the data via AJAX and then conditionally renders a pure functional child component when the data comes in.
class ParentThatFetches extends React.Component {
constructor () {
this.state = {};
}
componentDidMount () {
fetch('/some/async/data')
.then(resp => resp.json())
.then(data => this.setState({data}));
}
render () {
{this.state.data && (
<Child data={this.state.data} />
)}
}
}
const Child = ({data}) => (
<tr>
{data.map((x, i) => (<td key={i}>{x}</td>))}
</tr>
);
I didn't actually run it so their may be some minor errors, and if your data records have unique ids you should use those for the key attribute instead of the array index, but you get the jist.
UPDATE
Same thing but simpler and shorter using hooks:
const ParentThatFetches = () => {
const [data, updateData] = useState();
useEffect(() => {
const getData = async () => {
const resp = await fetch('some/url');
const json = await resp.json()
updateData(json);
}
getData();
}, []);
return data && <Child data={data} />
}
With the wrapper function below, delayed_render(), you can write asynchronous code inside a React component function:
function delayed_render(async_fun, deps=[]) {
const [output, setOutput] = useState()
useEffect(async () => setOutput(await async_fun()), deps)
return (output === undefined) ? null : output
}
This wrapper performs delayed rendering: it returns null on initial rendering attempt (to skip rendering of this particular component), then asynchronously calculates (useEffect()) the proper rendering output through a given async_fun() and invokes re-rendering to inject the final result to the DOM. The use of this wrapper is as simple as:
function Component(props) {
return delayed_render(async () => { /* any rendering code with awaits... */ })
}
For example:
function Component(props) {
return delayed_render(async () => {
const resp = await fetch(props.targetURL) // await here is OK!
const json = await resp.json()
return <Child data={json} />
})
}
UPDATE: added the deps argument. If your async_fun depends on props or state variables, all of them must be listed in deps to allow re-rendering. Note that passing deps=null (always re-render) is not an option here, because the output is a state variable, too, and would be implicitly included in dependencies, which would cause infinite re-rendering after the async_fun call completes.
This solution was inspired by, and is a generalization of, the Jared Smith's one.
I loop through this array like this:
{props.choosenMovie.characters.map((characters) => (
<p>{characters}</p> /* This displays the URL of course */
))}
These URL's include a name object which is what i want to display,
what is the best practice to do this?
This is how it is displayed on my application, but the desire is to display the name object from the URL's.
In useEffect, map thru your array of urls and make the api call and store the promises in an array. Use promise.all and update the state which will cause re-render.
In render method map thru the updated state and display the names.
see working demo
Code snippet
export default function App() {
const [char, setChar] = useState([
"https://swapi.dev/api/people/1/",
"https://swapi.dev/api/people/2/"
]);
const [people, setPeople] = useState([]);
useEffect(() => {
const promiseArray = [];
char.forEach(c => {
promiseArray.push(fetch(c).then(res => res.json()));
Promise.all(promiseArray).then(res => {
console.log("res", res);
setPeople(res);
});
});
}, []);
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
{people.map((p, i) => {
return <p key={i}>{p.name}</p>;
})}
</div>
);
}
I was working with that API some time ago, and the way I approached it (to display the names etc) was with Promise.all
so the snipped looked like
axios.get(`https://swapi.dev/api/${this.props.match.path.split('/')[1]}/${this.props.match.params.id}/`).then((res) => {
let characters = []
// get all characters in the movie data
let characterPromises = []
res.data.characters.forEach((character) => {
characterPromises.push(axios.get(character))
})
// Create list with all characters names and link to page
Promise.all(characterPromises).then((res) => {
res.forEach((character, i) => {
characters.push(<li key={i}><Link to={`/${character.data.url.split('api/')[1]}`}>{character.data.name}</Link></li>)
})
this.setState({
characters
})
})
})
}
then I just used the characters lists (from state) in the render method
i'm learning react and i getted API data but i don't know how can i display in React Google Charts.
The format to show in React Google Charts is like this:
['Germany', 200,0],
['United States', 300,0],
['Brazil', 400,0],
['Canada', 500,0],
['France', 600,0],
['RU', 700,0],
This is my code:
import React, { useState, useEffect } from "react";
import Chart from "react-google-charts";
const Map = ({ url }) => {
const [stats, setStats] = useState(null);
const [values, setValues] = useState([]);
const getData = async () => {
const data = await fetch(url);
const json = await data.json();
setStats(json);
for (const key in json) {
values.push([json[key].country, json[key].active, json[key].deaths]);
}
console.log(values);
// ["Afghanistan", 5717,169] --> I receive the data perfectly but i don't know how can i display it below
};
useEffect(() => {
getData();
}, []);
return (
<div className="col-xl-12 text-center mb-3">
<h4>Hover on map to see...</h4>
<Chart
width={"100%"}
height={"350px"}
chartType="GeoChart"
options={{
displayMode: "regions",
backgroundColor: "#81d4fa",
colorAxis: {
values: [1000, 10000, 50000, 100000, 1000000],
colors: ["#00bc8c", "#f39c12", "#e74c3c", "red", "darkred"],
},
}}
data={[
["Country", "Active cases", "Deaths"],
values.map(value => value + ",") //i tried this...
]}
// Note: you will need to get a mapsApiKey for your project.
// See: https://developers.google.com/chart/interactive/docs/basic_load_libs#load-settings
mapsApiKey="AIzaSyD-9tSrke72PouQMnMX-a7eZSW0jkFMBWY"
rootProps={{ "data-testid": "1" }}
/>
</div>
);
};
export default Map;
I commented the line with my fail tries.
I tried a lot of things but i can't show the data received
Thanks for your time
You need to run setValues after you load your json data. The changes you make to values don't take effect until you run setValues.
Also, although values feels like a list, perhaps consider avoiding using it directly with .push or similar. Instead, create a local copy of its current state, and then run setValues when you have the data for the map:
useEffect(() => {
const getData = async () => {
fetch(url)
.then(data => data.json())
.then(json => {
setStats(json);
let myVals = [];
for (let key in json) {
const { country, active, deaths } = json[key];
myVals.push([country, active, deaths]);
}
setValues(myVals);
});
};
getData();
}, []);
But if you're just reassigning that data with the same order then there could be a way to simplify it with map, something along the lines of:
const newValues = json.map(key => {
const { country, active, deaths } = json[key];
return [country, active, deaths];
});
setValues(newValues);
And you could safely condense that into one line, doesn't use push etc.
I also needed to make it only render the <Chart> element after the data was loaded, so I'm using a separate state variable for that, along the lines of:
[dataLoaded, setDataLoaded] = useState(false),
...
setValues(newValues);
setDataLoaded(true);
...
return <div>{
dataLoaded ?
<Chart
chartType="..."
data={[
['Country', 'Active', 'Deaths'],
...values
]} /> :
null}
</div>;
You could maybe just use the length of values or similar as a flag to refactor it, rather than a separate variable altogether.
I also ended up putting the column headers into the state variable as well, so that the data attribute for the <Chart> is just the name of the variable. Mine is:
[dataLoaded, setDataLoaded] = useState(false),
[googleData, setGoogleData] = useState([]),
...
const googleFormattedData = dataReturnedToGoogleData(0, data);
let va = googleData;
va[j] = googleFormattedData;
setGoogleData(va);
setDataLoaded(true);
...
(within a loop)
<Chart
data={
googleData[data.optionNumber]
} />
Not running setValues is the main issue with your version.
Personally I'd avoid using the word json as a variable name, even though it's not reserved when it's lowercase.
I have an API call returning a list of breweries. I want to convert it from its current format into my React state container. Something like:
state = {
breweries: [ {name: Foo,
long: 45,
lat: -39.239},
{name: Bar,
long: 47,
lat: -27.394}
]
}
I've managed to get it into an array of Javascript objects (I think?) in my console.log. See the pic below.
https://imgur.com/a/1M16hXC
I think I have to do something with the .map() function but I'm not sure what. Currently my code reads as follows:
class App extends Component {
state = {
breweries: null
};
componentDidMount() {
const breweries = axios
.get("https://api.openbrewerydb.org/breweries")
.then(response => response.data)
.then(list => this.setState({ breweries: list }));
}
printState = () => {
console.log(this.state.breweries);
};
render() {
const response = axios
.get("https://api.openbrewerydb.org/breweries")
.then(response => console.log(response));
return (
<div className="App">
{/* <button onClick={this.printBreweries}>Click Me</button> */}
<button onClick={this.printState}>Also click me</button>
</div>
);
}
}
Currently the best I can do is shown in the code above. ".then(list => this.setState({ breweries: list }))" causes console.log(this.state.breweries) to output the array of objects, but it's not quite what I want.
Help?
Yep, map will help you transform each item in the list e.g.
axios
.get("https://api.openbrewerydb.org/breweries")
.then(res => res.data.map(({ name, long, lat }) => ({ name, long, lat })))
.then(list => this.setState({ breweries: list }));
Note - the first then is making use of parameter destructuring, object initializer shorthand syntax and leveraging arrow functions ability to return a default result by parenthesising the body, in this case an object literal is being returned