how to destructure object of arrays from api and put into state? - javascript

So basically I'm learning how do api calls and I'm messing around with the the open weather api, trying to display the name and and temperature of a few different cities. The problem is the response is an object of arrays and trying to put all the names of the cities into to state so i can display them. Whats the best way to get all the names into state?
import "./App.css";
import React, { useEffect, useState } from "react";
import axios from "axios";
function App() {
const [cityName, setCityName] = useState("");
const [temp, setTemp] = useState("");
const [description, setDescription] = useState("");
const fetchData = () => {
//api variables (normily stored somewhere safer)
const lat = "35.320696399999996";
const lon = "-75.8232391";
const key = "115475ac7a8dda2a7e7ec0b27b93ce35";
const cnt = "5";
const url = `https://api.openweathermap.org/data/2.5/find?lat=${lat}&lon=${lon}&cnt=${cnt}&appid=${key}`;
axios
.get(url)
.then((res) => {
console.log(res.data.list);
})
.catch((err) => {
console.log(err);
});
};
useEffect(() => {
fetchData();
}, []);
return <div className="App"></div>;
}
export default App;

You can map the response list to a new array with just the properties you want. Update your component to have a single "data" state array to hold the mapped response values.
const [data, setData] = useState([]);
const fetchData = () => {
...
axios
.get(url)
.then((res) => {
console.log(res.data.list);
// destructure name, main.temp, and weather
const data = res.data.list.map(({ name, main: { temp }, weather }) => {
// weather is an array of length 1, but still provide fallback
const { description } = weather[0] ?? {};
return {
description,
name,
temp,
};
});
setData(data);
})
.catch((err) => {
console.log(err);
});
};
useEffect(() => {
fetchData();
}, []);

Related

How Can I call useCollection hook again with updated parameters

When My Home component(image above) mounts for the first time, it gives call to the hook "useCollection" with the arguments below, Now how can I call useCollection hook again with a different set of arguments for filtering my list,
It gives error calling to a function by using onClickButon handlers as well I m not able to use "UseState" as useState does not call the above hook again.
useCollection Hook
import React, { useEffect, useRef, useState } from "react";
import { projectFirestore } from "../firebase/config";
export const useCollections = (collection, _query, _orderBy) => {
const [documents, setDocuments] = useState(null);
const [error, setError] = useState(null);
const query = useRef(_query).current;
const orderBy = useRef(_orderBy).current;
useEffect(() => {
let ref = projectFirestore.collection(collection);
if (query) {
ref = ref.where(...query);
}
if (orderBy) {
ref = ref.orderBy(...orderBy);
}
const unsubscribe = ref.onSnapshot(
(snapshot) => {
let results = [];
snapshot.docs.forEach((doc) => {
results.push({ ...doc.data(), id: doc.id });
});
setDocuments(results);
setError(null);
},
(error) => {
console.log(error);
setError("could not fetch data");
}
);
return () => unsubscribe();
}, [collection, query, orderBy]);
return { documents, error };
};

How can I re-fetch an API automaticaly until data is fetched succesfully?

I have an API that sometimes doesn't work. I would like for the App to refetch automaticaly if this happens until it gets the necessary data. How can I do that? I'm thinking that maybe this could be done by using a dependency on the useEffect hook, but I'm not clear on how to do it.
Lets say we have this App component
export default function App() {
const [data, setData] = useState([])
useEffect(() => {
getData({ setData })
}, [])
return [
<h3>
{data[0].title}
</h3>
]
}
And this API component
const url = 'https://some-random-url.com/whatever-api'
export default function getData({ setData }) {
axios.get(url)
.then((response) => {
let dataArray = response.data.results
setData(dataArray)
})
.catch((error) => {
console.log(error)
})
}
If you were to do it with useEffect, you could pass an error counter state to getData function and increase it on error or empty data.
Then add to your useEffect dependency array to refetch.
But this certainly implies that you have to think further what you are wanting to do after a certain amount of retries, to avoid an infinite loop.
export default function App() {
const [data, setData] = useState([])
const [errCount, setErrCount] = useState(0)
useEffect(() => {
getData({ setData, errCount, setErrCount })
}, [errCount])
return [
<h3>
{data[0].title}
</h3>
]
}
And this API component
const url = 'https://some-random-url.com/whatever-api'
export default function getData({ setData, errCount, setErrCount }) {
axios.get(url)
.then((response) => {
let dataArray = response.data.results
setData(dataArray)
!dataArray.length && setErrCount(errCount+1);
})
.catch((error) => {
setErrCount(errCount+1);
console.log(error)
})
}

React show data from array of objects

Today I made a useFetch hook to get all the data from a certain category. As you can see on the image it's possible to see all the data in JSON format. Also you can see that it is in an array of objects. I was wondering how I can show this data in normal format like on the page. Most of the time I'm getting the error of data.name NULL. But as you can see the data is fetched correctly in JSON format on the image. I just don't understand how to show all this data normally. Any suggestions?
enter image description here
enter image description here
import React from "react";
import "../Style/menu.css";
import { useParams, withRouter } from "react-router-dom";
import useFetch from "../ApiService/useFetch";
import { render } from "#testing-library/react";
const Product = () => {
const { id } = useParams();
const { data, error, isPending } = useFetch("http://localhost:8080/products/category/" + id);
return (
<p>{JSON.stringify(data)}</p>
)
}
export default Product;
import { useState, useEffect } from "react";
const useFetch = (url) => {
const [data, setData] = useState(null);
const [isLoading, setIsPending] = useState(true);
const [error, setError ] = useState(null);
useEffect(() => {
fetch(url) //custom url so you can reuse it
.then(res => {
if(!res.ok) {
throw Error('could not fetch data');
}
return res.json();
})
.then(data => {
setData(data);
setIsPending(false)
setError(null)
})
.catch(err => {
setError(null)
setIsPending(false)
})
}, [url]);
return {data, isLoading, error} //use properties with custom hook
}
export default useFetch;
This might be helpful for you
...
const Product = () => {
const { id } = useParams();
const { data, error, isPending } = useFetch("http://localhost:8080/products/category/" + id);
return (
{ data && data.length &&
data.map((row) =>{
<p>row.name</p>
})
}
)
}
...
your useFetch is async, as I can see isPending variable, why don't you use this ?
const { id } = useParams();
const { data, error, isPending } = useFetch("http://localhost:8080/products/category/" + id);
return (
<p>{isPending ? null : JSON.stringify(data)}</p>
)

Arrays and Objects are being fetched and then reset to undefined. Using React/JavaScript/Json

I am new to JavaScript and React and am building a weather app that allows the user to save locations to their profile and retrieve the forecast data relevant to that specific location.
I need to display a list of buttons that display the only the names of the locations saved by the user logged in.
Currently, I'm fetching locations, current profile, and then mapping through the locations that was returned by the fetch to match up the location id's to the foreign keys saved to the profile. Right now, all of my fetches return empty arrays and objects. However, if I edit the code and save it, my React app re-renders correctly and the console prints the correct data. When I refresh the page I'm back to empty arrays and objects.
Here's my ProfilesProvider:
import React, { useState, createContext } from "react";
export const ProfileContext = createContext();
export const ProfileProvider = (props) => {
const [profiles, setProfiles] = useState([]);
const [currentProfile, setCurrentProfile] = useState({});
const getProfiles = () => {
return fetch("http://localhost:8088/profiles")
.then((res) => res.json())
.then((theProfiles) => setProfiles(theProfiles))
.then(console.log(profiles));
};
const getCurrentProfile = () => {
let id = localStorage.getItem("weathernet_user");
return fetch(`http://localhost:8088/profiles/${id}`)
.then((res) => res.json())
.then((theProfile) => {
return setCurrentProfile(theProfile);
})
.then(console.log(currentProfile));
};
Heres my LocationsProvider:
import React, { useState, createContext } from "react";
export const LocationContext = createContext();
export const LocationProvider = (props) => {
const apiURL = "http://localhost:8088";
const locationsURL = apiURL + "/locations";
const [locations, setLocations] = useState([]);
let id = localStorage.getItem("weathernet_user");
const getLocations = () => {
return fetch("http://localhost:8088/locations")
.then((res) => res.json())
.then(setLocations);
};
const addLocation = (locationObj) => {
return fetch("http://localhost:8088/locations", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(locationObj),
}).then(getLocations());
};
And here's the module where I'm implementing the code:
import React, { useContext, useEffect, useState } from "react";
import { LocationContext } from "./LocationsProvider";
import { ProfileContext } from "../profiles/ProfilesProvider";
import { useHistory } from "react-router-dom";
import "./Locations.css";
export const LocationList = () => {
const { locations, getLocations, deleteLocation } =
useContext(LocationContext);
const { profiles, getProfiles, currentProfile, getCurrentProfile } =
useContext(ProfileContext);
const [profile, setCurrentProfile] = useState({});
const [city, setCity] = useState("");
const [result, setResult] = useState({});
const [isHidden, setIsHidden] = useState(true);
const [buttonList, setButtonList] = useState([]);
const history = useHistory();
useEffect(() => {
Promise.all([
getLocations(),
getProfiles(),
getCurrentProfile(),
setCurrentProfile(),
]).then(() => {
console.log(currentProfile);
setButtonList(locationResults);
console.log(buttonList);
console.log(locationResults);
});
}, []);
const locationResults = (currentProfile.savedCityId || []).map((cityId) => {
return locations.find((location) => location.id === cityId);
});
I didn't include the lower half of the module because it's where im returning a form and I didn't want to clutter up the question too much

How to handle localstorage with result of a call to an external API in ReactJS?

The main gold is to make a serch bar from an external API. I'm using Context API to provide a global state, and a custom async hook to make a call to a pokeapi, I'm currently available, to store the data searched in localstorage, but the thing is that I store that data from a state that changes in a event, so when I reload the page the state is undefined, and sets the local storage value to undefined... there is a better approach to solve this?
context:
import React,{createContext, useEffect} from 'react'
import { usePokemonReducer } from './PokemonReducer'
import {FIND_POKEMON} from './Actions'
export const PokemonContext = createContext()
const PokemonProvider = ({children}) => {
const [state, dispatch] = usePokemonReducer(()=>{
const localData = localStorage.getItem('pokemons');
return localData ? JSON.parse(localData) : [];
});
const { pokemon } = state;
const findPokemon = (pokemon) => dispatch({ type: FIND_POKEMON, pokemon})
useEffect(() => {
localStorage.setItem('pokemons', JSON.stringify(pokemon.pokemon));
}, [pokemon]);
const providerValues = {
pokemon,
findPokemon,
}
return (
<PokemonContext.Provider value={providerValues}>
{children}
</PokemonContext.Provider>
)
}
export default PokemonProvider;
customAsyncHook:
import {useEffect, useState, useContext} from 'react'
import { PokemonContext } from '../../Services/Store/PokemonContext'
import {FIND_POKEMON} from '../../Services/Store/Actions'
import axios from 'axios'
const useAsyncHook = (id) => {
const [result, setResult] = useState();
const [loading, setLoading] = useState('false');
const { findPokemon } = useContext(PokemonContext)
useEffect(() => {
async function getPokemon() {
try {
setLoading('true');
const response = await axios(
`https://pokeapi.co/api/v2/pokemon/${id}`
);
setResult(response.data);
findPokemon({type:FIND_POKEMON, pokemon:response.data });
} catch (error) {
setLoading('null');
findPokemon({type:FIND_POKEMON, pokemon:null });
}
}
if (id !== "") {
getPokemon();
}
}, [id]);
return [result, loading];
}
export default useAsyncHook
You can just use if condition. if pokemon is undefined, you don't need to set item to localStorage.
useEffect(() => {
if (pokemon.pokemon !== undefined) {
localStorage.setItem('pokemons', JSON.stringify(pokemon.pokemon));
}
}, [pokemon]);

Categories